Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Goerli RPC Gateway for simulations #253

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.idea
76 changes: 68 additions & 8 deletions balancer-js/src/lib/utils/tenderlyHelper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import axios from 'axios';
import { MaxInt256 } from '@ethersproject/constants';
import { networkAddresses } from '@/lib/constants/config';
import { BalancerTenderlyConfig } from '@/types';
import {
Address,
BalancerTenderlyConfig,
TenderlyRpcResponse,
TenderlyRpcSimulationBlockNumber,
TenderlyRpcStateOverridesParameters,
TenderlyRpcTransactionParameters,
} from '@/types';

type StateOverrides = {
[address: string]: { value: { [key: string]: string } };
Expand All @@ -10,6 +17,7 @@ type StateOverrides = {
export default class TenderlyHelper {
private vaultAddress;
private tenderlyUrl;
private tenderlyRpcUrl = '';
private opts?;
private blockNumber: number | undefined;

Expand All @@ -24,11 +32,12 @@ export default class TenderlyHelper {
} else {
this.tenderlyUrl = 'https://api.balancer.fi/tenderly/';
}

if (tenderlyConfig?.accessKey) {
this.tenderlyRpcUrl = `https://goerli.gateway.tenderly.co/${tenderlyConfig.accessKey}`;
this.opts = {
headers: {
'X-Access-Key': tenderlyConfig.accessKey,
'Content-Type': 'application/json',
},
};
}
Expand All @@ -54,7 +63,7 @@ export default class TenderlyHelper {
...tokensOverrides,
...relayerApprovalOverride,
};
return this.simulateTransaction(
return this.simulateTransactionRpc(
to,
data,
userAddress,
Expand Down Expand Up @@ -103,6 +112,61 @@ export default class TenderlyHelper {
return simulatedTransactionOutput;
};

simulateTransactionRpc = async (
to: Address,
data: string,
userAddress: Address,
encodedStateOverrides: StateOverrides
): Promise<string> => {
try {
const transactionParams: TenderlyRpcTransactionParameters = {
to,
data,
from: userAddress,
};
const simulationBlockNumber: TenderlyRpcSimulationBlockNumber = `0x${this.blockNumber?.toString(
16
)}`;

const overrides: TenderlyRpcStateOverridesParameters = Object.fromEntries(
Object.keys(encodedStateOverrides).map((address) => {
// Object.fromEntries require format [key, value] instead of {key: value}
return [address, { stateDiff: encodedStateOverrides[address].value }];
})
);
const tenderlyParams = [
transactionParams,
simulationBlockNumber,
overrides,
];

const response = await axios.post(
this.tenderlyRpcUrl,
{
jsonrpc: '2.0',
method: 'tenderly_simulateTransaction',
params: tenderlyParams,
},
this.opts
);
const responseBody: TenderlyRpcResponse = response.data;
const callTraces = responseBody.result.trace.filter(
({ type, method }) => type === 'CALL' && method === 'multicall'
);
const lastCallTrace =
callTraces.length > 0
? callTraces[callTraces.length - 1]
: { output: '0x' };
return lastCallTrace.output;
} catch (error) {
console.log('simulate transaction rpc');
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
console.error(error.response.data.error);
throw error;
}
};

// Encode relayer approval state override
encodeRelayerApprovalOverride = async (
userAddress: string,
Expand All @@ -117,11 +181,7 @@ export default class TenderlyHelper {
},
};

const encodedStateOverrides = await this.requestStateOverrides(
stateOverrides
);

return encodedStateOverrides;
return await this.requestStateOverrides(stateOverrides);
};

// Encode token balances and allowances overrides to max value
Expand Down
107 changes: 107 additions & 0 deletions balancer-js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
import type { GraphQLArgs } from './lib/graphql';
import type { AprBreakdown } from '@/modules/pools/apr/apr';
import * as Queries from '@/modules/pools/queries/types';

export * from '@/modules/data/types';
export { Network, AprBreakdown };

Expand Down Expand Up @@ -347,3 +348,109 @@ export interface GraphQLQuery {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
attrs: any;
}

export interface TenderlyRpcTransactionParameters {
from?: Address; // hex encoded address "from"
to: Address; // hex encoded address "to"
gas?: number;
maxFeePerGas?: number; // max fee: The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei
maxPriorityFeePerGas?: number; // max priority fee: Maximum fee per gas the sender is willing to pay to miners in wei
gasPrice?: number; // The gas price willing to be paid by the sender in wei
value?: number;
data?: string;
accessList?: AccessListTuple[];
}

export interface AccessListTuple {
address: Address; // hex encoded address
storageKeys: string[]; // Array of 32 byte hex encoded storage key
}

export type TenderlyRpcSimulationBlockNumber =
| string // Block Number or...
| 'earliest' // Block tag
| 'finalized'
| 'safe'
| 'latest'
| 'pending';

export interface TenderlyRpcStateOverridesParameters {
[key: Address]: {
// the override specification
nonce?: string; // hex encoded 8 byte nonce override for the account
code?: string; // data of the code override for the account
balance?: string; // hex encoded 32 byte balance override for the account in wei
stateDiff: {
// mapping of storage key to storage value override
[key: string]: string; // key: the storage key -- value: the value override for the given storage key
};
};
}

export interface TenderlyRpcResponse {
id: number | string;
jsonrpc: string;
result: {
status: boolean | number;
gasUsed: string;
cumulativeGasUsed: string;
blockNumber: string;
type: string;
logsBloom: string;
logs: TenderlyRpcLog[];
trace: TenderlyRpcTrace[];
};
}

export interface TenderlyRpcLog {
name: string;
anonymous: boolean | number;
input: {
name: string;
type: string;
value?: string;
}[];
raw: {
address: string;
topics: string[];
data: string;
};
}

export interface TenderlyRpcTrace {
type:
| string
| 'CALL'
| 'CALLCODE'
| 'STATICCALL'
| 'DELEGATECALL'
| 'CREATE'
| 'CREATE2'
| 'SELFDESTRUCT';
from: Address;
to: Address;
gas: string;
gasUsed: string;
value: string;
error: string;
errorMessage: string;
input: string;
method: string | null;
decodedInput:
| {
value: string;
type: string;
name: string;
}[]
| null;
output: string;
decodedOutput:
| {
value: string;
type: string;
name: string;
}[]
| null;
subtraces: number;
traceAddress: number[];
}