Skip to content

Commit

Permalink
feat: add estimation and fee payment, update bls client
Browse files Browse the repository at this point in the history
  • Loading branch information
aquiladev committed Jun 4, 2023
1 parent 9b6732d commit 0d09b9d
Show file tree
Hide file tree
Showing 19 changed files with 197 additions and 537 deletions.
2 changes: 1 addition & 1 deletion packages/bls-snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@metamask/key-tree": "^6.2.1",
"async-mutex": "^0.4.0",
"bls-wallet-clients": "^0.8.2",
"bls-wallet-clients": "^0.9.0",
"deep-equal": "^2.2.0",
"ethers": "^5.7.2",
"uuid": "^9.0.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/bls-snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/aquiladev/bls-snap.git"
},
"source": {
"shasum": "c7B/56lWWe/CwHCh7BjnCPAfunYLR5upFlsHP2LtHmw=",
"shasum": "C7TlcF1PAaXMhYD1vgz5CoOzL6qES9BceUthCDs3zBs=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
1 change: 1 addition & 0 deletions packages/bls-snap/src/addErc20Token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export async function addErc20Token(params: ApiParams): Promise<Erc20Token> {
0,
),
isInternal: false,
isManageble: true,
};

await upsertErc20Token(
Expand Down
2 changes: 1 addition & 1 deletion packages/bls-snap/src/createAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export async function createAccount(params: ApiParams): Promise<Account> {
const account: BlsAccount = {
address: wallet.address,
publicKey: wallet.PublicKeyStr(),
privateKey: wallet.privateKey,
privateKey,
derivationPath,
addressIndex: aIndex,
name: `Account ${aIndex + 1}`,
Expand Down
14 changes: 6 additions & 8 deletions packages/bls-snap/src/getErc20TokenBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ export async function getErc20TokenBalance(params: ApiParams): Promise<string> {
const { tokenAddress, userAddress, chainId } =
requestParams as GetErc20TokenBalanceRequestParams;

if (!tokenAddress || !userAddress) {
if (!userAddress) {
throw new Error(
`The given token address and user address need to be non-empty string, got: ${JSON.stringify(
`The given user address need to be non-empty string, got: ${JSON.stringify(
requestParams,
)}`,
);
}

if (!ethers.utils.isAddress(tokenAddress)) {
if (tokenAddress && !ethers.utils.isAddress(tokenAddress)) {
throw new Error(`The given token address is invalid: ${tokenAddress}`);
}

Expand All @@ -43,11 +43,9 @@ export async function getErc20TokenBalance(params: ApiParams): Promise<string> {
`getErc20Balance:\nerc20Address: ${tokenAddress}\nuserAddress: ${userAddress}`,
);

const balance = await evmUtils.getErc20TokenBalance(
network,
tokenAddress,
userAddress,
);
const balance = tokenAddress
? await evmUtils.getErc20TokenBalance(network, tokenAddress, userAddress)
: await evmUtils.getBalance(network, userAddress);
console.log(`getErc20Balance:\nresp: ${JSON.stringify(balance)}`);

return BigNumber.from(balance).toHexString();
Expand Down
3 changes: 2 additions & 1 deletion packages/bls-snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { OnRpcRequestHandler } from '@metamask/snaps-types';
import { Mutex } from 'async-mutex';

import { ApiParams, ApiRequestParams } from './types/snapApi';
import { addTestToken, upsertNetwork } from './utils/snapUtils';
import { addNativeToken, addTestToken, upsertNetwork } from './utils/snapUtils';
import * as config from './utils/config';
import { getAddressKeyDeriver } from './utils/crypto';
import { addAction } from './addAction';
Expand Down Expand Up @@ -65,6 +65,7 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
const networks = config.getNetworks();
for (const network of networks) {
await upsertNetwork(network, snap, mutex, state);
await addNativeToken(network, snap, mutex, state);
await addTestToken(network, snap, mutex, state);
}

Expand Down
22 changes: 11 additions & 11 deletions packages/bls-snap/src/networks/arbitrum-goerli.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"parameters": {},
"addresses": {
"create2Deployer": "0x036d996D6855B83cd80142f2933d8C2617dA5617",
"precompileCostEstimator": "0x22E4a5251C1F02de8369Dd6f192033F6CB7531A4",
"blsLibrary": "0xF8a11BA6eceC43e23c9896b857128a4269290e39",
"verificationGateway": "0xae7DF242c589D479A5cF8fEA681736e0E0Bb1FB9",
"blsExpander": "0x4473e39a5F33A83B81387bb5F816354F04E724a3",
"utilities": "0x76cE3c1F2E6d87c355560fCbd28ccAcAe03f95F6",
"testToken": "0x5081a39b8A5f0E35a8D959395a630b68B74Dd30f"
"safeSingletonFactory": "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
"precompileCostEstimator": "0x6eb8F8d661eFe36daB11147830A1e690249bB830",
"verificationGateway": "0xE25229F29BAD62B1198F05F32169B70a9edc84b8",
"blsExpander": "0x30Ae0d96Ba812F53F69deDefd8b472ce43af23A5",
"utilities": "0x4bD2E4e99B50A2a9e6b9dABfA3C8dCD1f885F008",
"testToken": "0x30e69AfbE8c2E8ECe8d6920512452C8d13F8962A"
},
"auxiliary": {
"chainid": 421613,
"domain": "0x0054159611832e24cdd64c6a133e71d373c5f8553dde6c762e6bffe707ad83cc",
"genesisBlock": 1206441,
"deployedBy": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"version": "bc3d1463f163b742026f951a2574016966b5c857"
"walletDomain": "0xb8b27430df81daac9be5d58dcca2e4dd4aae72a95c3fb3f0ee9b77e5357f6bec",
"bundleDomain": "0xde7ef148d5c129757b1740441010559c8a17199c68ae144446131e62c0b953ff",
"genesisBlock": 18628869,
"deployedBy": "0x9D5e5038c47da189f8C67A587c66a952a8b45bAb",
"version": "0d52ddb20f2a395f3fff13130459a8561533024b"
}
}
22 changes: 11 additions & 11 deletions packages/bls-snap/src/networks/optimism-goerli.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"parameters": {},
"addresses": {
"create2Deployer": "0x036d996D6855B83cd80142f2933d8C2617dA5617",
"precompileCostEstimator": "0x22E4a5251C1F02de8369Dd6f192033F6CB7531A4",
"blsLibrary": "0xF8a11BA6eceC43e23c9896b857128a4269290e39",
"verificationGateway": "0x643468269B044bA84D3F2190F601E3579d3236BB",
"blsExpander": "0x7E24FBC8A777418f519fdaB565168f19b1C8e2Fa",
"utilities": "0x76cE3c1F2E6d87c355560fCbd28ccAcAe03f95F6",
"testToken": "0xD2a233b4F7b49641eef0Aa0E4927A299737b39CB"
"safeSingletonFactory": "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
"precompileCostEstimator": "0x6eb8F8d661eFe36daB11147830A1e690249bB830",
"verificationGateway": "0xE25229F29BAD62B1198F05F32169B70a9edc84b8",
"blsExpander": "0x30Ae0d96Ba812F53F69deDefd8b472ce43af23A5",
"utilities": "0x4bD2E4e99B50A2a9e6b9dABfA3C8dCD1f885F008",
"testToken": "0xf2149397e902D6D4E958d027F323Fd37F7Bd11eF"
},
"auxiliary": {
"chainid": 420,
"domain": "0x0054159611832e24cdd64c6a133e71d373c5f8553dde6c762e6bffe707ad83cc",
"genesisBlock": 4101595,
"deployedBy": "0xe5deB006644a6FF28AC361Fe896D351a40AeA198",
"version": "1d35f4e80a5f5316158e7f01e61a721fe62f0103"
"walletDomain": "0x802fb686c57e5392e94fbeb158cf09881e634e133d41525be792fa87c47cfeaa",
"bundleDomain": "0xa5112f769feb56d1fa9bed9680d850f1362a970daff8bdffc7a231ed0fc02a84",
"genesisBlock": 8830153,
"deployedBy": "0x9D5e5038c47da189f8C67A587c66a952a8b45bAb",
"version": "090405ee0a630f4fb9f0c20ae995240f7474507d"
}
}
52 changes: 41 additions & 11 deletions packages/bls-snap/src/sendBundle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ethers } from 'ethers';
import { BigNumber, ethers, utils } from 'ethers';
import { ApiParams, SendBundleRequestParams } from './types/snapApi';
import * as config from './utils/config';
import * as snapUtils from './utils/snapUtils';
Expand Down Expand Up @@ -61,20 +61,50 @@ export async function sendBundle(params: ApiParams): Promise<Bundle> {
const wallet = await getWallet(network, account.privateKey);
const nonce = await wallet.Nonce();

const iface = new utils.Interface(['function sendEthToTxOrigin()']);
const feeAction = {
ethValue: 1,
// Provide 1 wei with this action so that the fee transfer to
// tx.origin can be included in the gas estimate.
contractAddress: network.config.addresses.utilities,
encodedFunction: iface.encodeFunctionData('sendEthToTxOrigin'),
};

const _actions = actions.map((op) => {
return {
ethValue: op.value,
contractAddress: op.contractAddress,
encodedFunction: op.encodedFunction,
};
});

const aggregator = getAggregator(network);

// Estimate the fee required to send the bundle
const estimateFeeBundle = await wallet.signWithGasEstimate({
nonce,
actions: [..._actions, feeAction],
});
const feeEstimate = await aggregator.estimateFee(estimateFeeBundle);

// Add a safety premium to the fee to account for fluctuations in gas estimation
const safetyDivisor = 5;
const feeRequired = BigNumber.from(feeEstimate.feeRequired);
const safetyPremium = feeRequired.div(safetyDivisor);
const safeFee = feeRequired.add(safetyPremium);

// All of the actions in a bundle are atomic, if one
// action fails they will all fail.
const _bundle = wallet.sign({
const _bundle = await wallet.signWithGasEstimate({
nonce,
actions: actions.map((op) => {
return {
ethValue: op.value,
contractAddress: op.contractAddress,
encodedFunction: op.encodedFunction,
};
}),
actions: [
..._actions,
{
...feeAction,
ethValue: safeFee, // fee amount
},
],
});

const aggregator = getAggregator(network);
const result = await aggregator.add(_bundle);

if ('failures' in result) {
Expand Down
1 change: 1 addition & 0 deletions packages/bls-snap/src/types/snapState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type Erc20Token = {
symbol: string;
decimals: number;
isInternal: boolean;
isManageble: boolean;
};

export type Action = {
Expand Down
8 changes: 8 additions & 0 deletions packages/bls-snap/src/utils/evmUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ export async function getErc20TokenBalance(
const [balance] = utils.defaultAbiCoder.decode(['uint256'], result);
return balance;
}

export async function getBalance(
network: Network,
account: string,
): Promise<BigNumber> {
const provider = getProvider(network);
return provider.getBalance(account);
}
22 changes: 22 additions & 0 deletions packages/bls-snap/src/utils/snapUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,27 @@ export function getAccounts(
return state[chainId]?.accounts;
}

export async function addNativeToken(
network: Network,
snap: any,
mutex: Mutex,
state: SnapState,
) {
if (!network?.config?.addresses?.testToken) {
return;
}

const token: Erc20Token = {
address: '',
name: 'Ether',
symbol: 'ETH',
decimals: 18,
isInternal: true,
isManageble: false,
};
await upsertErc20Token(token, network.chainId, snap, mutex, state);
}

export async function addTestToken(
network: Network,
snap: any,
Expand All @@ -193,6 +214,7 @@ export async function addTestToken(
symbol: 'TOK',
decimals: 18,
isInternal: true,
isManageble: true,
};
await upsertErc20Token(token, network.chainId, snap, mutex, state);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/bls-snap/test/getBundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ describe('getBundle', () => {
const result = await getBundle(apiParams);

expect(walletStub.rpcStubs.snap_manageState).to.have.been.called;
expect(result).to.be.eql({
expect({
...BUNDLE_ZERO,
...BUNDLE_RECEIPT_ZERO,
});
}).to.be.includes(result);
});

it('should throw error if bundle not found', async () => {
Expand Down
26 changes: 24 additions & 2 deletions packages/bls-snap/test/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,9 @@ export const ACCOUNT_ZERO: BlsAccount = {

export const BLS_ACCOUNT_ZERO: bls.BlsWalletWrapper = {
address: ACCOUNT_ZERO.address,
privateKey: ACCOUNT_ZERO.privateKey,
PublicKeyStr: () => ACCOUNT_ZERO.publicKey,
Nonce: () => Promise.resolve(BigNumber.from(0)),
sign: (_: bls.Operation) => {
signWithGasEstimate: async (_: Omit<bls.Operation, 'gas'>, __ = 0) => {
return {} as bls.Bundle;
},
} as bls.BlsWalletWrapper;
Expand All @@ -79,6 +78,7 @@ export const ERC20_TOKEN_ZERO: Erc20Token = {
symbol: 'TT0',
decimals: 18,
isInternal: false,
isManageble: true,
};

export const ERC20_TOKEN_ONE: Erc20Token = {
Expand All @@ -87,6 +87,7 @@ export const ERC20_TOKEN_ONE: Erc20Token = {
symbol: 'TT1',
decimals: 18,
isInternal: true,
isManageble: true,
};

export const ACTION_ZERO: Action = {
Expand Down Expand Up @@ -122,8 +123,29 @@ export const BUNDLE_RECEIPT_ZERO: BundleReceipt = {
bundleHash: BUNDLE_HASH_ZERO,
blockHash: '0x1234',
blockNumber: 1,
to: ZERO_ADDRESS,
from: ZERO_ADDRESS,
contractAddress: ZERO_ADDRESS,
cumulativeGasUsed: BigNumber.from(1),
effectiveGasPrice: BigNumber.from(1),
gasUsed: BigNumber.from(1),
logs: [],
events: [],
logsBloom: '',
status: 1,
byzantium: true,
confirmations: 1,
root: '0x0',
type: 1,
};

export const AGGREGATOR_MOCK: bls.Aggregator = {
add: (_: bls.Bundle) => Promise.resolve({ hash: BUNDLE_HASH_ZERO }),
estimateFee: (_: bls.Bundle) =>
Promise.resolve({
feeType: 'ether',
feeDetected: '1',
feeRequired: '1',
successes: [true],
}),
} as bls.Aggregator;
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ export const BundleListItemView = ({ bundle }: Props) => {
const explorerUrl = networks.items[networks.activeNetwork]?.explorerUrl;
const [showActions, setShowActions] = useState(false);
const status = getBundleStatus(bundle);
const statusColor = status.toLowerCase() === 'pending' ? 'orange' : 'green';
const statusColor =
// eslint-disable-next-line no-nested-ternary
status.toLowerCase() === 'success'
? 'green'
: status.toLowerCase().startsWith('error:')
? 'red'
: 'orange';

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Bundle } from '@aquiladev/bls-snap/src/types/snapState';

export const getBundleStatus = (bundle: Bundle): string => {
return bundle.blockNumber ? 'Success' : 'Pending';
// eslint-disable-next-line no-nested-ternary
return bundle.blockNumber
? 'Success'
: bundle.error
? `Error: ${bundle.error}`
: 'Pending';
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export const BundlesListView = () => {
const address = wallet.accounts?.[
wallet.activeAccount
] as unknown as string;
const pandingBundles = (wallet.bundles || []).filter((b) => !b.blockNumber);
const pandingBundles = (wallet.bundles || []).filter(
(b) => !b.blockNumber && !b.error,
);
if (chainId && address && pandingBundles?.length) {
clearTimeout(timeoutHandle.current); // cancel the timeout that was in-flight
timeoutHandle.current = setTimeout(async () => {
Expand Down

0 comments on commit 0d09b9d

Please sign in to comment.