-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Sample application before adding GSN support.
This branch is the basic implementation, before adding any GSN support. - The "workshop-with-gsn" is the same app with GSN support - The "workshop-with-paymaster" is the same app with GSN and custom paymaster
- Loading branch information
1 parent
933dd87
commit 6f34bae
Showing
12 changed files
with
2,930 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/node_modules/ | ||
/.idea/ | ||
/.DS_Store | ||
/build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# GSN v3 integration workshop | ||
|
||
### (Base branch - before adding any GSN support) | ||
|
||
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` | ||
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.. | ||
|
||
You can see the integrations as GitHub pull requests: | ||
|
||
1. [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 | ||
required. On mainnet, you need a custom paymaster, such as a token paymaster that allow users to pay for gas in tokens, | ||
and exchanging them for ETH ETH on Uniswap. Dapps will want to develop their own custom paymaster in order, for example | ||
to subsidize gas fees for new users during the onboarding process. | ||
|
||
### Further reading | ||
|
||
GSNv3 integration tutorial: https://docs.opengsn.org/tutorials | ||
|
||
Documentation explaining how everything works: https://docs.opengsn.org/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/** | ||
* SPDX-License-Identifier:MIT | ||
*/ | ||
pragma solidity ^0.8.7; | ||
|
||
contract CaptureTheFlag { | ||
|
||
event FlagCaptured(address previousHolder, address currentHolder); | ||
|
||
address public currentHolder = address(0); | ||
|
||
function captureTheFlag() external { | ||
address previousHolder = currentHolder; | ||
|
||
currentHolder = msg.sender; | ||
|
||
emit FlagCaptured(previousHolder, currentHolder); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* SPDX-License-Identifier:MIT | ||
*/ | ||
pragma solidity >=0.4.25 <0.9.0; | ||
|
||
contract Migrations { | ||
address public owner; | ||
uint public last_completed_migration; | ||
|
||
modifier restricted() { | ||
if (msg.sender == owner) _; | ||
} | ||
|
||
constructor() { | ||
owner = msg.sender; | ||
} | ||
|
||
function setCompleted(uint completed) public restricted { | ||
last_completed_migration = completed; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const Migrations = artifacts.require("Migrations"); | ||
|
||
module.exports = function (deployer) { | ||
deployer.deploy(Migrations); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const CaptureTheFlag = artifacts.require('CaptureTheFlag') | ||
|
||
module.exports = async function (deployer) { | ||
await deployer.deploy(CaptureTheFlag) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "gsn-sample-workshop", | ||
"version": "3.0.0", | ||
"description": "Simple example of how to use GSNv3", | ||
"private": true, | ||
"dependencies": { | ||
"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'", | ||
"test": "truffle test", | ||
"compile": "truffle compile", | ||
"build": "./ui/build.sh", | ||
"start": "truffle deploy && yarn build && yarn serve ./build/html" | ||
}, | ||
"author": "Dror Tirosh", | ||
"license": "GPL" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const CaptureTheFlag = artifacts.require('CaptureTheFlag') | ||
|
||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' | ||
|
||
contract("CaptureTheFlag", async accounts => { | ||
|
||
let account | ||
let captureFlagContract | ||
|
||
before(async () => { | ||
captureFlagContract = await CaptureTheFlag.new(); | ||
|
||
account = accounts[0] | ||
}) | ||
|
||
it('Runs without 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"); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#! /bin/bash -e | ||
|
||
rm -rf ./build/html/ | ||
mkdir ./build/html/ | ||
|
||
browserify ./ui/index.js -o ./build/html/bundle.js | ||
cp ./ui/index.html ./build/html/ | ||
|
||
echo "Done building \"./build/html\" at `date`" | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<html> | ||
<head> | ||
|
||
<title>GSNv3 Test</title> | ||
|
||
<script src="bundle.js"> | ||
</script> | ||
|
||
</head> | ||
<body> | ||
<h2>GSNv3 Test App</h2> | ||
|
||
<script> | ||
window.app.initContract().then(function ({contractAddress, network}) { | ||
console.log('CaptureTheFlag contract', contractAddress) | ||
console.log(`identified network: ${JSON.stringify(network)}`) | ||
document.getElementById('capture_button').disabled = false | ||
}) | ||
</script> | ||
|
||
<button id="capture_button" disabled onClick="window.app.contractCall()"> | ||
Capture the flag | ||
</button> | ||
|
||
<hr> | ||
<div id="logview"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
const ethers = require('ethers') | ||
|
||
const contractArtifact = require('../build/contracts/CaptureTheFlag.json') | ||
const contractAbi = contractArtifact.abi | ||
|
||
let theContract | ||
let provider | ||
|
||
async function initContract() { | ||
|
||
if (!window.ethereum) { | ||
throw new Error('provider not found') | ||
} | ||
window.ethereum.on('accountsChanged', () => { | ||
console.log('acct'); | ||
window.location.reload() | ||
}) | ||
window.ethereum.on('chainChanged', () => { | ||
console.log('chainChained'); | ||
window.location.reload() | ||
}) | ||
const networkId = await window.ethereum.request({method: 'net_version'}) | ||
|
||
provider = new ethers.providers.Web3Provider(window.ethereum) | ||
|
||
const network = await provider.getNetwork() | ||
const artifactNetwork = contractArtifact.networks[networkId] | ||
if (!artifactNetwork) | ||
throw new Error('Can\'t find deployment on network ' + networkId) | ||
const contractAddress = artifactNetwork.address | ||
theContract = new ethers.Contract( | ||
contractAddress, contractAbi, provider.getSigner()) | ||
|
||
await listenToEvents() | ||
return {contractAddress, network} | ||
} | ||
|
||
async function contractCall() { | ||
await window.ethereum.send('eth_requestAccounts') | ||
|
||
const txOptions = {gasPrice: await provider.getGasPrice()} | ||
const transaction = await theContract.captureTheFlag(txOptions) | ||
const hash = transaction.hash | ||
console.log(`Transaction ${hash} sent`) | ||
const receipt = await transaction.wait() | ||
console.log(`Mined in block: ${receipt.blockNumber}`) | ||
} | ||
|
||
let logview | ||
|
||
function log(message) { | ||
message = message.replace(/(0x\w\w\w\w)\w*(\w\w\w\w)\b/g, '<b>$1...$2</b>') | ||
if (!logview) { | ||
logview = document.getElementById('logview') | ||
} | ||
logview.innerHTML = message + "<br>\n" + logview.innerHTML | ||
} | ||
|
||
async function listenToEvents() { | ||
|
||
theContract.on('FlagCaptured', (previousHolder, currentHolder, rawEvent) => { | ||
log(`Flag Captured from ${previousHolder} by ${currentHolder}`) | ||
console.log(`Flag Captured from ${previousHolder} by ${currentHolder}`) | ||
}) | ||
} | ||
|
||
window.app = { | ||
initContract, | ||
contractCall, | ||
log | ||
} | ||
|
Oops, something went wrong.