Skip to content

Commit ac9da1e

Browse files
committed
With a custom paymaster
This PR adds a custom paymaster to accept gas payments.
1 parent 76025e5 commit ac9da1e

File tree

7 files changed

+293
-85
lines changed

7 files changed

+293
-85
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# GSN v3 integration workshop
22

3-
### (This branch already contains frontend and contract integration with GSN)
3+
### (This branch adds a custom paymaster to our GSN-enabled sample)
44

55
This sample dapp emits an event with the last account that clicked on the "capture the flag" button. We will integrate
66
this dapp to work gaslessly with GSN v3. This will allow an externally owned account without ETH to capture the flag by
77
signing a meta transaction.
88

99
### To run the sample:
1010

11+
1112
1. first clone and `yarn install`
1213
2. run `yarn gsn-with-ganache` to start a node, and also deploy GSN contracts and start a relayer service.
1314
3. Make sure you have Metamask installed, and pointing to "localhost"
@@ -17,8 +18,8 @@ signing a meta transaction.
1718

1819
You can see the integrations as GitHub pull requests:
1920

20-
1. (this branch) [Basic: Minimum viable GSN integration](https://github.com/opengsn/workshop/pull/1/files)
21-
2. [Advanced: Write your own custom Paymaster](https://github.com/opengsn/workshop/pull/2/files_)
21+
1. [Basic: Minimum viable GSN integration](https://github.com/opengsn/workshop/pull/1/files)
22+
2. (this branch) [Advanced: Write your own custom Paymaster](https://github.com/opengsn/workshop/pull/2/files_)
2223

2324
Note: on testnet we maintain a public service "pay for everything" paymaster so writing your own is not strictly
2425
required. On mainnet, you need a custom paymaster, such as a token paymaster that allow users to pay for gas in tokens,

contracts/ImportArtifacts.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.7;
3+
4+
//"import" it into our project for Truffle to generate artifacts
5+
import "@opengsn/paymasters/contracts/WhitelistPaymaster.sol";
6+

migrations/2_deploy_contracts.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
11
const CaptureTheFlag = artifacts.require('CaptureTheFlag')
2+
const WhitelistPaymaster = artifacts.require('WhitelistPaymaster')
23

3-
module.exports = async function (deployer) {
4+
module.exports = async function (deployer, network, accounts) {
45
const forwarder = require('../build/gsn/Forwarder').address
56
await deployer.deploy(CaptureTheFlag, forwarder)
67

78
console.log(`Deployed CTF at ${CaptureTheFlag.address} with forwarder ${forwarder}`)
9+
10+
await deployer.deploy(WhitelistPaymaster)
11+
const relayHubAddress = require('../build/gsn/RelayHub.json').address
12+
const paymaster = await WhitelistPaymaster.deployed()
13+
await paymaster.setRelayHub(relayHubAddress)
14+
await paymaster.setTrustedForwarder(forwarder)
15+
16+
// This is the first ganache address, when started with "ganache-cli -d"
17+
// you can add your metamask address here.
18+
await paymaster.whitelistSender('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1')
19+
20+
21+
// you can also add addresses by running `truffle console` and then running:
22+
// const pm = await WhitelistPaymaster.deployed()
23+
// pm.whitelistSender('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1')
24+
25+
await web3.eth.sendTransaction({from: accounts[0], to: paymaster.address, value: 1e18})
26+
console.log(`1 ETH deposited to Paymaster(${WhitelistPaymaster.address})`)
827
}

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"dependencies": {
77
"@opengsn/contracts": "^3.0.0-alpha.6",
88
"@opengsn/dev": "^3.0.0-alpha.6",
9+
"@opengsn/paymasters": "^3.0.0-alpha.6",
910
"@opengsn/provider": "^3.0.0-alpha.6",
1011
"browserify": "^17.0.0",
1112
"ethers": "^5.6.8",
@@ -14,8 +15,8 @@
1415
"serve": "^13.0.0"
1516
},
1617
"scripts": {
17-
"ganache": "yarn run ganache-cli -d --networkId 1337 --chainId 1337",
18-
"gsn-with-ganache": "run-with-testrpc -d --networkId 1337 --chainId 1337 'gsn start'",
18+
"ganache": "yarn run ganache-cli -d --chainId 1337",
19+
"gsn-with-ganache": "run-with-testrpc -d --chainId 1337 'gsn start'",
1920
"test": "truffle test",
2021
"compile": "truffle compile",
2122
"build": "./ui/build.sh",

test/testcontracts.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,31 @@ const {RelayProvider} = require('@opengsn/provider')
22
const {GsnTestEnvironment} = require('@opengsn/dev')
33

44
const CaptureTheFlag = artifacts.require('CaptureTheFlag')
5+
const WhitelistPaymaster = artifacts.require('WhitelistPaymaster')
56

67
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
78

89
contract("CaptureTheFlag", async accounts => {
910

1011
let account
1112
let captureFlagContract
13+
let whitelistPaymaster
1214

1315
before(async () => {
14-
const {forwarderAddress, paymasterAddress} = GsnTestEnvironment.loadDeployment()
16+
const {forwarderAddress, relayHubAddress} = GsnTestEnvironment.loadDeployment()
17+
18+
const whitelistPaymaster = await WhitelistPaymaster.new()
19+
await whitelistPaymaster.setTrustedForwarder(forwarderAddress)
20+
await whitelistPaymaster.setRelayHub(relayHubAddress)
21+
await web3.eth.sendTransaction({from: accounts[0], to: whitelistPaymaster.address, value: 1e18})
1522

1623
captureFlagContract = await CaptureTheFlag.new(forwarderAddress);
1724

1825
const gsnProvider = await RelayProvider.newProvider({
1926
provider: web3.currentProvider,
2027
config: {
2128
loggerConfiguration: {logLevel: 'error'},
22-
paymasterAddress,
29+
paymasterAddress: whitelistPaymaster.address,
2330
//these 2 params are needed only for ganache:
2431
methodSuffix: '',
2532
jsonStringifyRequest: false,
@@ -33,6 +40,8 @@ contract("CaptureTheFlag", async accounts => {
3340
// default ganache accounts all have eth.
3441
// test from a different account, without any eth
3542
account = gsnProvider.newAccount().address
43+
44+
await whitelistPaymaster.whitelistSender(account)
3645
})
3746

3847
it('Runs with GSN', async () => {
@@ -41,4 +50,13 @@ contract("CaptureTheFlag", async accounts => {
4150
assert.equal(res.logs[0].args.previousHolder, ZERO_ADDRESS, "Wrong previous flag holder");
4251
assert.equal(res.logs[0].args.currentHolder, account, "Wrong current flag holder");
4352
});
53+
54+
it('Paymaster should reject different account', async () => {
55+
await captureFlagContract.captureTheFlag({from: accounts[2]})
56+
.then(() => {
57+
throw new Error('should revert')
58+
})
59+
.catch(() => {
60+
})
61+
});
4462
});

ui/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
const ethers = require('ethers')
22
const {RelayProvider} = require('@opengsn/provider')
3+
let whitelistPaymasterAddress
34

4-
const paymasterAddress = require('../build/gsn/Paymaster').address
5+
const WhitelistPaymasterArtifact = require('../build/contracts/WhitelistPaymaster.json')
56
const contractArtifact = require('../build/contracts/CaptureTheFlag.json')
67
const contractAbi = contractArtifact.abi
78

@@ -24,11 +25,14 @@ async function initContract() {
2425
})
2526
const networkId = await window.ethereum.request({method: 'net_version'})
2627

28+
whitelistPaymasterAddress = WhitelistPaymasterArtifact.networks[networkId].address
29+
console.log('Using whitelistPaymaster at', whitelistPaymasterAddress)
30+
2731
gsnProvider = await RelayProvider.newProvider({
2832
provider: window.ethereum,
2933
config: {
30-
loggerConfiguration: {logLevel: 'debug'},
31-
paymasterAddress
34+
//loggerConfiguration: { logLevel: 'error' },
35+
paymasterAddress: whitelistPaymasterAddress
3236
}
3337
}).init()
3438

0 commit comments

Comments
 (0)