Skip to content

Commit

Permalink
Merge pull request #25 from hemilabs/support-multi-chain
Browse files Browse the repository at this point in the history
Hemi fixes and improved multi-chain support
  • Loading branch information
gabmontes authored Apr 16, 2024
2 parents 8f489ef + 2be1b6d commit 0e58898
Show file tree
Hide file tree
Showing 32 changed files with 377 additions and 219 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,39 @@ Then open the browser at http://localhost:3000.
1. Add it to the `Utilities` component so it appears in the home page.
1. Go from there!

## Testing

### Merkle claims

Go to `packages/merkle-box-lib` and create a list of recipients as a JSON file:

```json
[
{
"account": "0x0000000000000000000000000000000000000010",
"amount": "1000000000000000000"
},
{
"account": "0x0000000000000000000000000000000000000020",
"amount": "2000000000000000000"
}
]
```

Create the dataset:

```sh
node scripts/create-dataset.js recipients.json > dataset.json
```

And copy the data set file to the development web server at `site/public`.

Set `NODE_URL`, `MNEMONIC` and create the claim group:

```sh
node scripts/create-claim-group.js WETH http://localhost:3000/test20241231.json 2024-12-31
```

## End-to-end tests

Set the following environment variables: `BASE_NODE_URL`, `MNEMONIC`.
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
"@next/next/no-html-link-for-pages": [
"warn",
"site/pages"
],
"complexity": [
"warn",
20
]
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/erc-20-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"deps:check": "dependency-check --no-dev ."
},
"dependencies": {
"@uniswap/default-token-list": "^2.0.0",
"token-list": "^1.0.0",
"debug": "^4.3.1",
"erc-20-abi": "^1.0.0"
},
Expand Down
67 changes: 40 additions & 27 deletions packages/erc-20-lib/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ const createErc20 = function (web3, address, options = {}) {

const contract = new web3.eth.Contract(abi, address)

const chainIdPromise = web3.eth.getChainId()

const wethContractPromise = chainIdPromise.then(chainId =>
weth.getContract(web3, chainId)
)

const safeGas = gas => Math.ceil(gas * gasFactor)

const estimateGasAndSend = (method, transactionOptions) =>
Expand All @@ -31,6 +37,8 @@ const createErc20 = function (web3, address, options = {}) {
const totalSupply = () => contract.methods.totalSupply().call()

return {
getAddress: () => contract.options.address,

getInfo: () =>
Promise.all([
contract.methods.symbol().call(),
Expand Down Expand Up @@ -73,40 +81,45 @@ const createErc20 = function (web3, address, options = {}) {
totalSupply,

wrapEther: value =>
estimateGasAndSend(weth.getContract(web3).methods.deposit(), {
from,
gasPrice,
value
}),
wethContractPromise.then(wethContract =>
estimateGasAndSend(wethContract.methods.deposit(), {
from,
gasPrice,
value
})
),

unwrapEther: value =>
estimateGasAndSend(weth.getContract(web3).methods.withdraw(value), {
from,
gasPrice
}),
wethContractPromise.then(wethContract =>
estimateGasAndSend(wethContract.methods.withdraw(value), {
from,
gasPrice
})
),

wrappedEtherBalanceOf: address =>
weth
.getContract(web3)
.methods.balanceOf(address || from)
.call(),
wethContractPromise.then(wethContract =>
wethContract.methods.balanceOf(address || from).call()
),

swapEther: value =>
estimateGasAndSend(
uniswap
.getRouterContract(web3)
.methods.swapExactETHForTokens(
'1',
[tokenAddress('WETH'), address],
chainIdPromise.then(chainId =>
estimateGasAndSend(
uniswap
.getRouterContract(web3)
.methods.swapExactETHForTokens(
'1',
[tokenAddress('WETH', chainId), address],
from,
Math.round(Date.now() / 1000) + 60
),
{
from,
Math.round(Date.now() / 1000) + 60
),
{
from,
gas: safeGas(100000),
gasPrice,
value: value.toString()
}
gas: safeGas(100000),
gasPrice,
value: value.toString()
}
)
)
}
}
Expand Down
11 changes: 4 additions & 7 deletions packages/erc-20-lib/src/token-address.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
'use strict'

const { tokens } = require('@uniswap/default-token-list')
const { findTokenBySymbol } = require('token-list')

const tokenAddress = function (symbol, extraTokens = []) {
const tokenData = tokens.concat(extraTokens)
const token =
tokenData.find(t => t.symbol === symbol) ||
tokenData.find(t => t.symbol.toLowerCase() === symbol.toLowerCase())
return token && token.address
const tokenAddress = function (symbol, chainId) {
const token = findTokenBySymbol(symbol, chainId)
return token?.address
}

module.exports = tokenAddress
6 changes: 3 additions & 3 deletions packages/erc-20-lib/src/weth.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
const tokenAddress = require('./token-address')

const abi = require('./abis/weth9.json')
const address = tokenAddress('WETH')

const getContract = web3 => new web3.eth.Contract(abi, address)
const getContract = (web3, chainId) =>
new web3.eth.Contract(abi, tokenAddress('WETH', chainId))

module.exports = { abi, address, getContract }
module.exports = { abi, getContract }
99 changes: 58 additions & 41 deletions packages/merkle-box-lib/scripts/create-claim-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* USDC http://dataset.url/file.json 2021-12-31
*/

/* eslint-disable no-console */

'use strict'

require('dotenv').config()
Expand All @@ -17,58 +19,73 @@ const Web3 = require('web3')

const createMerkleBox = require('..')

const [token, datasetUrl, unlock] = process.argv.slice(2)
const { ACCOUNT = '0', MNEMONIC = '', NODE_URL, PRIVATE_KEYS } = process.env

const provider = new HDWalletProvider({
addressIndex: Number.parseInt(process.env.ACCOUNT) || 0,
mnemonic: process.env.MNEMONIC,
numberOfAddresses: 1,
providerOrUrl: process.env.NODE_URL
})
const from = provider.getAddress(0)
const web3 = new Web3(provider)
const merkleBoxAddress = createMerkleBox.addresses.mainnet
const merkleBox = createMerkleBox(web3, merkleBoxAddress, { from })
const [token, datasetUrl, unlock] = process.argv.slice(2)

const tokenAddress = token.startsWith('0x')
? token
: createErc20.util.tokenAddress(token)
const getContractObjects = () =>
// @ts-ignore ts(2351)
new Web3(NODE_URL).eth.getChainId().then(function (chainId) {
const provider = new HDWalletProvider({
addressIndex: Number.parseInt(ACCOUNT),
chainId,
numberOfAddresses: 1,
providerOrUrl: NODE_URL,
...(PRIVATE_KEYS
? { privateKeys: PRIVATE_KEYS.split(',') }
: { mnemonic: MNEMONIC })
})
const from = provider.getAddress(0)
// @ts-ignore ts(2351)
const web3 = new Web3(provider)
const tokenAddress = token.startsWith('0x')
? token
: createErc20.util.tokenAddress(token, chainId)
const erc20 = createErc20(web3, tokenAddress, { from })
const merkleBoxAddress = createMerkleBox.addresses[chainId]
const merkleBox = createMerkleBox(web3, merkleBoxAddress, { from })
return { erc20, merkleBox, provider }
})

const memo = `datasetUri=${datasetUrl}`
const parseDataSet = () =>
fetch(datasetUrl)
.then(res => res.json())
.then(function (recipients) {
const root = createMerkleBox.util.bufferToHex(
createMerkleBox.util.calcMerkleTree(recipients).getRoot()
)
const total = recipients
.reduce((sum, recipient) => sum + BigInt(recipient.amount), BigInt(0))
.toString()
return {
root,
total
}
})

const toTimestamp = str =>
/^[0-9]+$/.test(str)
? Number.parseInt(str)
: Math.round(new Date(str).getTime() / 1000)

return fetch(datasetUrl)
.then(res => res.json())
.then(function (recipients) {
const total = recipients
.reduce((sum, recipient) => sum + BigInt(recipient.amount), BigInt(0))
.toString()
const root = createMerkleBox.util.bufferToHex(
createMerkleBox.util.calcMerkleTree(recipients).getRoot()
)
return Promise.all([
total,
root,
createErc20(web3, tokenAddress, { from }).approve(merkleBoxAddress, total)
])
})
.then(([total, root]) =>
merkleBox.newClaimsGroup(
tokenAddress,
total,
root,
toTimestamp(unlock),
memo
)
Promise.all([getContractObjects(), parseDataSet()])
.then(([{ erc20, merkleBox, provider }, { root, total }]) =>
erc20
.approve(merkleBox.getAddress(), total)
.then(() =>
merkleBox.newClaimsGroup(
erc20.getAddress(),
total,
root,
toTimestamp(unlock),
`datasetUri=${datasetUrl}`
)
)
.finally(function () {
provider.engine.stop()
})
)
.then(function (receipt) {
console.log(receipt.events.NewMerkle.returnValues)
})
.catch(console.error)
.finally(function () {
provider.engine.stop()
})
4 changes: 3 additions & 1 deletion packages/merkle-box-lib/scripts/create-dataset.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/**
* Usage:
*
* node scripts/create-dataset.js ./recipients.json > dataset.json
* node scripts/create-dataset.js recipients.json > dataset.json
*/

/* eslint-disable no-console */

'use strict'

const createMerkleBox = require('..')
Expand Down
4 changes: 4 additions & 0 deletions packages/merkle-box-lib/src/addresses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"1": "0xe67516417a934b27cf0c14868f8165b1bc94bf73",
"743111": "0xc2dA346E2f655B1013cB0f405Ca1b99553F7a580"
}
6 changes: 6 additions & 0 deletions packages/merkle-box-lib/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const debug = require('debug')('merkle-box')

const abi = require('./abi.json')
const addresses = require('./addresses.json')
const util = require('./util')

const createMerkleBox = function (web3, address, options = {}) {
Expand Down Expand Up @@ -38,14 +39,19 @@ const createMerkleBox = function (web3, address, options = {}) {
{ from, ...txOps }
)

const getAddress = () => merkleBox.options.address

return {
claim,
getAddress,
getHolding,
isClaimable,
newClaimsGroup
}
}

createMerkleBox.addresses = addresses

createMerkleBox.util = util

module.exports = createMerkleBox
Loading

0 comments on commit 0e58898

Please sign in to comment.