Skip to content

Commit

Permalink
Add GSN provider support
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
drortirosh committed Jul 28, 2022
1 parent c7c9e8b commit 76025e5
Show file tree
Hide file tree
Showing 7 changed files with 4,933 additions and 123 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
# GSN v3 integration workshop

### (Base branch - before adding any GSN support)
### (This branch already contains frontend and contract integration with GSN)

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


### To run the sample:

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

You can see the integrations as GitHub pull requests:

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

Note: on testnet we maintain a public service "pay for everything" paymaster so writing your own is not strictly
Expand Down
12 changes: 10 additions & 2 deletions contracts/CaptureTheFlag.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@
*/
pragma solidity ^0.8.7;

contract CaptureTheFlag {
import "@opengsn/contracts/src/ERC2771Recipient.sol";

contract CaptureTheFlag is ERC2771Recipient {

event FlagCaptured(address previousHolder, address currentHolder);

address public currentHolder = address(0);

constructor(address forwarder) {
_setTrustedForwarder(forwarder);
}

string public override versionRecipient = "3.0.0";

function captureTheFlag() external {
address previousHolder = currentHolder;

currentHolder = msg.sender;
currentHolder = _msgSender();

emit FlagCaptured(previousHolder, currentHolder);
}
Expand Down
5 changes: 4 additions & 1 deletion migrations/2_deploy_contracts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const CaptureTheFlag = artifacts.require('CaptureTheFlag')

module.exports = async function (deployer) {
await deployer.deploy(CaptureTheFlag)
const forwarder = require('../build/gsn/Forwarder').address
await deployer.deploy(CaptureTheFlag, forwarder)

console.log(`Deployed CTF at ${CaptureTheFlag.address} with forwarder ${forwarder}`)
}
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
"description": "Simple example of how to use GSNv3",
"private": true,
"dependencies": {
"@opengsn/contracts": "^3.0.0-alpha.6",
"@opengsn/dev": "^3.0.0-alpha.6",
"@opengsn/provider": "^3.0.0-alpha.6",
"browserify": "^17.0.0",
"ethers": "^5.6.8",
"ganache-cli": "^6.12.2",
"run-with-testrpc": "^0.3.1",
"serve": "^13.0.0"
},
"scripts": {
"ganache": "yarn run ganache-cli -d --chainId 1337",
"gsn-with-ganache": "run-with-testrpc -d --chainId 1337 'gsn start'",
"ganache": "yarn run ganache-cli -d --networkId 1337 --chainId 1337",
"gsn-with-ganache": "run-with-testrpc -d --networkId 1337 --chainId 1337 'gsn start'",
"test": "truffle test",
"compile": "truffle compile",
"build": "./ui/build.sh",
Expand Down
29 changes: 25 additions & 4 deletions test/testcontracts.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
const {RelayProvider} = require('@opengsn/provider')
const {GsnTestEnvironment} = require('@opengsn/dev')

const CaptureTheFlag = artifacts.require('CaptureTheFlag')

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

before(async () => {
captureFlagContract = await CaptureTheFlag.new();
const {forwarderAddress, paymasterAddress} = GsnTestEnvironment.loadDeployment()

captureFlagContract = await CaptureTheFlag.new(forwarderAddress);

const gsnProvider = await RelayProvider.newProvider({
provider: web3.currentProvider,
config: {
loggerConfiguration: {logLevel: 'error'},
paymasterAddress,
//these 2 params are needed only for ganache:
methodSuffix: '',
jsonStringifyRequest: false,
}
}).init()

account = accounts[0]
//during test, "artifacts" initialize the the default web3
// we need to replace the provider.
CaptureTheFlag.web3.setProvider(gsnProvider)

// default ganache accounts all have eth.
// test from a different account, without any eth
account = gsnProvider.newAccount().address
})

it('Runs without GSN', async () => {
it('Runs with GSN', async () => {
const res = await captureFlagContract.captureTheFlag({from: account});
assert.equal(res.logs[0].event, "FlagCaptured", "Wrong event");
assert.equal(res.logs[0].args.previousHolder, ZERO_ADDRESS, "Wrong previous flag holder");
assert.equal(res.logs[0].args.currentHolder, account, "Wrong current flag holder");
});

});
13 changes: 12 additions & 1 deletion ui/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
const ethers = require('ethers')
const {RelayProvider} = require('@opengsn/provider')

const paymasterAddress = require('../build/gsn/Paymaster').address
const contractArtifact = require('../build/contracts/CaptureTheFlag.json')
const contractAbi = contractArtifact.abi

let theContract
let provider
let gsnProvider

async function initContract() {

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

provider = new ethers.providers.Web3Provider(window.ethereum)
gsnProvider = await RelayProvider.newProvider({
provider: window.ethereum,
config: {
loggerConfiguration: {logLevel: 'debug'},
paymasterAddress
}
}).init()

provider = new ethers.providers.Web3Provider(gsnProvider)

const network = await provider.getNetwork()
const artifactNetwork = contractArtifact.networks[networkId]
Expand Down
Loading

0 comments on commit 76025e5

Please sign in to comment.