Skip to content

Commit db14e26

Browse files
committed
Add GSN provider support
minimal set of changes to add GSN support to the contract and the dApp so that calling user no longer needs eth. Actual payment is done by the default "accept everything" paymaster contract.
1 parent 6f34bae commit db14e26

File tree

7 files changed

+5008
-118
lines changed

7 files changed

+5008
-118
lines changed

README.md

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

3-
### (Base branch - before adding any GSN support)
3+
### (This branch already contains frontend and contract integration with GSN)
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

9-
109
### To run the sample:
1110

1211
1. first clone and `yarn install`
13-
2. run `yarn ganache`
12+
2. run `yarn gsn-with-ganache` to start a node, and also deploy GSN contracts and start a relayer service.
1413
3. Make sure you have Metamask installed, and pointing to "localhost"
1514
4. In a different window, run `yarn start`, to deploy the contract, and start the UI
1615
5. Start a browser pointing to "http://localhost:3000"
17-
6. Click the "Capture the Flag" button. Notice that you do need an account with eth for that..
16+
6. Click the "Capture the Flag" button. Notice that you don't need eth in your account: You only sign the transaction.
1817

1918
You can see the integrations as GitHub pull requests:
2019

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

2423
Note: on testnet we maintain a public service "pay for everything" paymaster so writing your own is not strictly

contracts/CaptureTheFlag.sol

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,24 @@
33
*/
44
pragma solidity ^0.8.7;
55

6-
contract CaptureTheFlag {
6+
import "@opengsn/contracts/src/ERC2771Recipient.sol";
7+
8+
contract CaptureTheFlag is ERC2771Recipient {
79

810
event FlagCaptured(address previousHolder, address currentHolder);
911

1012
address public currentHolder = address(0);
1113

14+
constructor(address forwarder) {
15+
_setTrustedForwarder(forwarder);
16+
}
17+
18+
string public override versionRecipient = "3.0.0";
19+
1220
function captureTheFlag() external {
1321
address previousHolder = currentHolder;
1422

15-
currentHolder = msg.sender;
23+
currentHolder = _msgSender();
1624

1725
emit FlagCaptured(previousHolder, currentHolder);
1826
}

migrations/2_deploy_contracts.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
const CaptureTheFlag = artifacts.require('CaptureTheFlag')
22

33
module.exports = async function (deployer) {
4-
await deployer.deploy(CaptureTheFlag)
4+
const forwarder = require('../build/gsn/Forwarder').address
5+
await deployer.deploy(CaptureTheFlag, forwarder)
6+
7+
console.log(`Deployed CTF at ${CaptureTheFlag.address} with forwarder ${forwarder}`)
58
}

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
"description": "Simple example of how to use GSNv3",
55
"private": true,
66
"dependencies": {
7+
"@opengsn/contracts": "^3.0.0-beta.1",
8+
"@opengsn/dev": "^3.0.0-beta.1",
9+
"@opengsn/provider": "^3.0.0-beta.1",
710
"browserify": "^17.0.0",
811
"ethers": "^5.6.8",
912
"ganache-cli": "^6.12.2",
1013
"run-with-testrpc": "^0.3.1",
1114
"serve": "^13.0.0"
1215
},
1316
"scripts": {
14-
"ganache": "yarn run ganache-cli -d --chainId 1337",
15-
"gsn-with-ganache": "run-with-testrpc -d --chainId 1337 'gsn start'",
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'",
1619
"test": "truffle test",
1720
"compile": "truffle compile",
1821
"build": "./ui/build.sh",

test/testcontracts.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
const {RelayProvider} = require('@opengsn/provider')
2+
const {GsnTestEnvironment} = require('@opengsn/dev')
3+
14
const CaptureTheFlag = artifacts.require('CaptureTheFlag')
25

36
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
@@ -8,16 +11,34 @@ contract("CaptureTheFlag", async accounts => {
811
let captureFlagContract
912

1013
before(async () => {
11-
captureFlagContract = await CaptureTheFlag.new();
14+
const {forwarderAddress, paymasterAddress} = GsnTestEnvironment.loadDeployment()
15+
16+
captureFlagContract = await CaptureTheFlag.new(forwarderAddress);
17+
18+
const gsnProvider = await RelayProvider.newProvider({
19+
provider: web3.currentProvider,
20+
config: {
21+
loggerConfiguration: {logLevel: 'error'},
22+
paymasterAddress,
23+
//these 2 params are needed only for ganache:
24+
methodSuffix: '',
25+
jsonStringifyRequest: false,
26+
}
27+
}).init()
1228

13-
account = accounts[0]
29+
//during test, "artifacts" initialize the the default web3
30+
// we need to replace the provider.
31+
CaptureTheFlag.web3.setProvider(gsnProvider)
32+
33+
// default ganache accounts all have eth.
34+
// test from a different account, without any eth
35+
account = gsnProvider.newAccount().address
1436
})
1537

16-
it('Runs without GSN', async () => {
38+
it('Runs with GSN', async () => {
1739
const res = await captureFlagContract.captureTheFlag({from: account});
1840
assert.equal(res.logs[0].event, "FlagCaptured", "Wrong event");
1941
assert.equal(res.logs[0].args.previousHolder, ZERO_ADDRESS, "Wrong previous flag holder");
2042
assert.equal(res.logs[0].args.currentHolder, account, "Wrong current flag holder");
2143
});
22-
2344
});

ui/index.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
const ethers = require('ethers')
2+
const {RelayProvider} = require('@opengsn/provider')
23

4+
const paymasterAddress = require('../build/gsn/Paymaster').address
35
const contractArtifact = require('../build/contracts/CaptureTheFlag.json')
46
const contractAbi = contractArtifact.abi
57

68
let theContract
79
let provider
10+
let gsnProvider
811

912
async function initContract() {
1013

@@ -21,7 +24,15 @@ async function initContract() {
2124
})
2225
const networkId = await window.ethereum.request({method: 'net_version'})
2326

24-
provider = new ethers.providers.Web3Provider(window.ethereum)
27+
gsnProvider = await RelayProvider.newProvider({
28+
provider: window.ethereum,
29+
config: {
30+
loggerConfiguration: {logLevel: 'debug'},
31+
paymasterAddress
32+
}
33+
}).init()
34+
35+
provider = new ethers.providers.Web3Provider(gsnProvider)
2536

2637
const network = await provider.getNetwork()
2738
const artifactNetwork = contractArtifact.networks[networkId]

0 commit comments

Comments
 (0)