quasvel
Version:
Access and interact with Aragon Organizations and their apps.
67 lines (61 loc) • 2.81 kB
text/typescript
import { Contract, providers as ethersProviders } from 'ethers'
import { forwarderAbi } from './abis'
export const FORWARD_SIG = '0xd948d468' // function forward(bytes)
/**
* Tells if the given calldata (as a bytes string) is a valid invocation of
* `forward(bytes)`.
*
* It will return true only if the given calldata starts with the forward
* function signature and follows with at least an empty bytes array properly
* ABI encoded following the convention [offset][length][data].
*
* @param {string} calldata Calldata encoded as an array of bytes
*/
export function isValidForwardCall(calldata: string): boolean {
// Drop the 0x starting notation if there is one
calldata = calldata.replace(/^0x/, '')
// First 4 bytes represent the function selector
const selector = calldata.substring(0, 8)
// Drop selector and grab the argument data
const evmscriptData = calldata.substring(8)
// Since arrays of bytes are encoded following the [offset][length][data]
// format, we expect it to have at least two words length (empty data scenario)
return `0x${selector}` === FORWARD_SIG && evmscriptData.length >= 128
}
/**
* Parse the evmscript of a forward call following the byte ABI encoding
* convention [offset][length][data].
*
* @param {string} calldata Calldata encoded as an array of bytes
* @returns {string} Array of bytes representing the forwarded evmscript
*/
export function parseForwardCall(calldata: string): string {
// Drop the 0x starting notation if there is one
calldata = calldata.replace(/^0x/, '')
// Drop function selector and grab the argument data (of type bytes)
const evmscriptData = calldata.substring(8)
// Parse first word of the bytes array to get data offset
// (it's stored as bytes so we need to parse in hex first and then multiply by 2)
const offset = parseInt(`0x${evmscriptData.substring(0, 64)}`, 16) * 2
// The first word in the data is its length (uint256); actual data starts after
const startIndex = offset + 64
// Parse length of the data stored
// (it's stored as bytes so we need to parse in hex first and then multiply by 2)
const dataLength =
parseInt(`0x${evmscriptData.substring(offset, startIndex)}`, 16) * 2
// Grab the data stored in the bytes array
return `0x${evmscriptData.substring(startIndex, startIndex + dataLength)}`
}
/**
* Whether the `sender` can use the `forwarder` to invoke `script`.
*/
export function canForward(
forwarderAddress: string,
sender: string,
script: string,
provider: ethersProviders.Provider
): Promise<boolean> {
// Check if a token approval pretransaction is needed due to the forwarder requiring a fee
const forwarder = new Contract(forwarderAddress, forwarderAbi, provider)
return forwarder.canForward(sender, script).catch(() => false)
}