Skip to content

Commit

Permalink
misc. optimizations, improved_try_VERSION (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
devanoneth authored Oct 16, 2024
1 parent 866c23b commit 93cf8ee
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 59 deletions.
14 changes: 7 additions & 7 deletions schema.graphql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
type Wallet @entity {
"Unique identifier of the wallet (lowercase Safe{Wallet} address)"
id : ID!
"Unique identifier of the wallet (lowercase Safe{Wallet} address) TODO: check if lowercase is still needed"
id : Bytes!

"Address that created the contract"
creator : Bytes!
Expand Down Expand Up @@ -32,13 +32,13 @@ type Wallet @entity {
"Number of confirmations required to execute a transaction"
threshold : BigInt!

"List of sucessful and failed transactions executed from the wallet"
transactions : [Transaction!]!
"List of successful and failed transactions executed from the wallet"
transactions : [Transaction!]! @derivedFrom(field: "wallet")
}

type Transaction @entity {
"Unique identifier of the transaction = keccak256(concat(walletAddress, txHash))"
id : ID!
id : Bytes!

"Timestamp when the transaction was executed"
timestamp : BigInt
Expand Down Expand Up @@ -94,8 +94,8 @@ type Transaction @entity {
"Present if the transaction is from a Safe module (in this case some other fields won't be available as not all fields are used in the Safe module context)"
module : Bytes

"Wallet parent"
wallet : Wallet! @derivedFrom(field: "transactions")
"Safe{Wallet} parent"
wallet : Wallet!
}

enum TransactionStatus {
Expand Down
3 changes: 1 addition & 2 deletions src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function handleProxyCreation(

let maybeL2 = isL2 ? "+L2" : "";

let wallet = new Wallet(walletAddress.toHex());
let wallet = new Wallet(walletAddress);
wallet.creator = event.transaction.from;
wallet.network = dataSource.network();
wallet.timestamp = event.block.timestamp;
Expand All @@ -56,7 +56,6 @@ function handleProxyCreation(
// TODO: check if this impacts the nonce
// TODO: index the first transaction
wallet.currentNonce = zeroBigInt();
wallet.transactions = [];
wallet.save();

// Instantiate a new datasource for the Safe{Wallet}
Expand Down
97 changes: 57 additions & 40 deletions src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,28 @@ import {
export function handleChangedMasterCopy(event: ChangedMasterCopy): void {
let walletAddr = event.address;
let safeInstance = GnosisSafe.bind(walletAddr);
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
wallet.singleton = event.params.singleton;

// it would be nice if we could change the data source for the wallet at this point from Safe to SafeL2 but it doesn't seem to be possible
let maybeL2 = isL2Wallet(event.params.singleton) ? "+L2" : "";
wallet.version = safeInstance.VERSION() + maybeL2;

let version = improved_try_VERSION(safeInstance);
if (version.isEmpty) {
log.warning(
"handleChangedMasterCopy::Wallet: {} transaction {} - cannot get VERSION (empty bytes)",
[walletAddr.toHexString(), event.transaction.hash.toHexString()]
);
} else if (version.reverted) {
log.warning(
"handleChangedMasterCopy::Wallet: {} transaction {} - cannot get VERSION (reverted)",
[walletAddr.toHexString(), event.transaction.hash.toHexString()]
);
} else {
wallet.version = version.value + maybeL2;
}

wallet.save();
} else {
Expand All @@ -55,7 +69,7 @@ export function handleChangedMasterCopy(event: ChangedMasterCopy): void {

export function handleAddedOwner(event: AddedOwner): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let owners = wallet.owners;
Expand All @@ -71,7 +85,7 @@ export function handleAddedOwner(event: AddedOwner): void {

export function handleRemovedOwner(event: RemovedOwner): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let owners = wallet.owners;
Expand All @@ -90,7 +104,7 @@ export function handleRemovedOwner(event: RemovedOwner): void {

export function handleChangedThreshold(event: ChangedThreshold): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
wallet.threshold = event.params.threshold;
Expand All @@ -106,7 +120,7 @@ export function handleExecutionFromModuleSuccess(
event: ExecutionFromModuleSuccess
): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let transaction = getTransaction(walletAddr, event.transaction.hash);
Expand All @@ -116,9 +130,6 @@ export function handleExecutionFromModuleSuccess(
transaction.hash = event.transaction.hash;
transaction.timestamp = event.block.timestamp;
transaction.save();

wallet = addTransactionToWallet(<Wallet>wallet, transaction);
wallet.save();
} else {
log.warning("handleExecutionFromModuleSuccess::Wallet {} not found", [
walletAddr.toHexString(),
Expand All @@ -130,7 +141,7 @@ export function handleExecutionFromModuleFailure(
event: ExecutionFromModuleFailure
): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let transaction = getTransaction(walletAddr, event.transaction.hash);
Expand All @@ -151,7 +162,7 @@ export function handleExecTransactionFromModule(
call: ExecTransactionFromModuleCall
): void {
let walletAddr = call.to;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let transaction = getTransaction(walletAddr, call.transaction.hash);
Expand All @@ -162,9 +173,6 @@ export function handleExecTransactionFromModule(
transaction.operation =
call.inputs.operation == 0 ? "CALL" : "DELEGATE_CALL";
transaction.save();

wallet = addTransactionToWallet(<Wallet>wallet, transaction);
wallet.save();
} else {
log.warning("handleExecTransactionFromModule::Wallet {} not found", [
walletAddr.toHexString(),
Expand All @@ -176,7 +184,7 @@ export function handleSafeModuleTransaction(
event: SafeModuleTransaction
): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let transaction = getTransaction(walletAddr, event.transaction.hash);
Expand All @@ -187,9 +195,6 @@ export function handleSafeModuleTransaction(
transaction.operation =
event.params.operation == 0 ? "CALL" : "DELEGATE_CALL";
transaction.save();

wallet = addTransactionToWallet(<Wallet>wallet, transaction);
wallet.save();
} else {
log.warning("handleSafeModuleTransaction::Wallet {} not found", [
walletAddr.toHexString(),
Expand All @@ -199,7 +204,7 @@ export function handleSafeModuleTransaction(

export function handleExecutionSuccess(event: ExecutionSuccess): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let transaction = getTransaction(walletAddr, event.params.txHash);
Expand All @@ -210,9 +215,6 @@ export function handleExecutionSuccess(event: ExecutionSuccess): void {
transaction.txHash = event.params.txHash;
transaction.payment = event.params.payment;
transaction.save();

wallet = addTransactionToWallet(<Wallet>wallet, transaction);
wallet.save();
} else {
log.warning("handleExecutionSuccess::Wallet {} not found", [
walletAddr.toHexString(),
Expand All @@ -222,7 +224,7 @@ export function handleExecutionSuccess(event: ExecutionSuccess): void {

export function handleExecutionFailure(event: ExecutionFailure): void {
let walletAddr = event.address;
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let transaction = getTransaction(walletAddr, event.params.txHash);
Expand All @@ -233,9 +235,6 @@ export function handleExecutionFailure(event: ExecutionFailure): void {
transaction.txHash = event.params.txHash;
transaction.payment = event.params.payment;
transaction.save();

wallet = addTransactionToWallet(<Wallet>wallet, transaction);
wallet.save();
} else {
log.warning("handleExecutionFailure::Wallet {} not found", [
walletAddr.toHexString(),
Expand Down Expand Up @@ -284,28 +283,46 @@ export function handleSafeMultiSigTransaction(
*/

function getTransaction(wallet: Address, transctionHash: Bytes): Transaction {
let id = crypto.keccak256(concat(wallet, transctionHash));
let id = Bytes.fromByteArray(
crypto.keccak256(concat(wallet, transctionHash))
);

let transaction = Transaction.load(id.toHexString());
let transaction = Transaction.load(id);
if (transaction == null) {
transaction = new Transaction(id.toHexString());
transaction = new Transaction(id);
transaction.wallet = wallet;
}

return transaction as Transaction;
}

function addTransactionToWallet(
wallet: Wallet,
transaction: Transaction
): Wallet {
let transactions = wallet.transactions;
/**
* Improved version of the GnosisSafe.try_VERSION function which handles the case where
* the version is empty bytes.
* @param walletInstance A GnosisSafe contract instance
* @returns ImprovedCallResult<string>
*/
function improved_try_VERSION(
walletInstance: GnosisSafe
): ImprovedCallResult<string> {
let result = walletInstance.tryCall("VERSION", "VERSION():(string)", []);
if (result.reverted) {
return new ImprovedCallResult();
}

if (transactions.indexOf(transaction.id) == -1) {
transactions.push(transaction.id);
wallet.transactions = transactions;
let value = result.value;
if (
value[0].kind == ethereum.ValueKind.STRING &&
value[0].toString().length == 0
) {
// consider a version of 0 length as a non-existent version
// this can happen when Safes are bricked
// e.g. This random Safe:
// https://etherscan.io/address/0xec34bf8f41bc951071a501502e1e60af0cc9f9d6
return ImprovedCallResult.emptyValue<string>();
}

return wallet;
return ImprovedCallResult.fromValue(value[0].toString());
}

/**
Expand Down Expand Up @@ -351,7 +368,7 @@ function handleTransaction(
refundReceiver: Address,
signatures: Bytes
): void {
let wallet = Wallet.load(walletAddr.toHex());
let wallet = Wallet.load(walletAddr);

if (wallet != null) {
let walletInstance = GnosisSafe.bind(walletAddr);
Expand Down Expand Up @@ -399,6 +416,7 @@ function handleTransaction(
]
);
}

transaction.value = value;
transaction.to = to;
transaction.signatures = signatures;
Expand All @@ -411,7 +429,6 @@ function handleTransaction(
transaction.refundReceiver = refundReceiver;
transaction.save();

wallet = addTransactionToWallet(<Wallet>wallet, transaction);
wallet.currentNonce = currentNonce.value;
wallet.save();
}
Expand Down
Binary file modified tests/.bin/factory.wasm
Binary file not shown.
Binary file modified tests/.bin/wallet.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/.latest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "0.6.0",
"timestamp": 1728580668689
"timestamp": 1729008721629
}
4 changes: 1 addition & 3 deletions tests/factory.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Address, Bytes, ethereum, BigInt, log } from "@graphprotocol/graph-ts";
import { Address, Bytes, ethereum } from "@graphprotocol/graph-ts";
import {
describe,
test,
clearStore,
createMockedFunction,
afterEach,
newMockCall,
newMockEvent,
assert,
} from "matchstick-as/assembly/index";
import { Wallet } from "../generated/schema";
import { ProxyCreation as ProxyCreation_v1_1_1 } from "../generated/GnosisSafeProxyFactory_v1_1_1/GnosisSafeProxyFactory";
import { ProxyCreation as ProxyCreation_v1_3_0 } from "../generated/GnosisSafeProxyFactory_v1_3_0/GnosisSafeProxyFactory";
import { ProxyCreation as ProxyCreation_v1_4_1 } from "../generated/SafeProxyFactory_v1_4_1/SafeProxyFactory";
Expand Down
9 changes: 3 additions & 6 deletions tests/wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe("handleExecTransaction tests", () => {
ethereum.Value.fromBytes(Bytes.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000000")),
]);

let wallet = new Wallet(normalSafe.toHex());
let wallet = new Wallet(normalSafe);
wallet.creator = Address.fromString(
"0xB6BdD4F0839eF6791eC1c77Cf29B10592d514624"
);
Expand All @@ -69,7 +69,6 @@ describe("handleExecTransaction tests", () => {
]);
wallet.threshold = BigInt.fromI32(1);
wallet.currentNonce = BigInt.fromI32(1);
wallet.transactions = [];
wallet.save();

let call = changetype<ExecTransactionCall>(newMockCall());
Expand Down Expand Up @@ -124,7 +123,7 @@ describe("handleExecTransaction tests", () => {
);
createMockedFunction(brokenSafe, "nonce", "nonce():(uint256)").reverts();

let wallet = new Wallet(brokenSafe.toHex());
let wallet = new Wallet(brokenSafe);
wallet.creator = Address.fromString(
"0xB6BdD4F0839eF6791eC1c77Cf29B10592d514624"
);
Expand All @@ -145,7 +144,6 @@ describe("handleExecTransaction tests", () => {
]);
wallet.threshold = BigInt.fromI32(1);
wallet.currentNonce = zeroBigInt();
wallet.transactions = [];
wallet.save();

let call = changetype<ExecTransactionCall>(newMockCall());
Expand Down Expand Up @@ -202,7 +200,7 @@ describe("handleExecTransaction tests", () => {
ethereum.Value.fromBytes(Bytes.fromHexString("0x")),
]);

let wallet = new Wallet(brokenSafe.toHex());
let wallet = new Wallet(brokenSafe);
wallet.creator = Address.fromString(
"0xfA54B4085811aef6ACf47D51B05FdA188DEAe28b"
);
Expand All @@ -227,7 +225,6 @@ describe("handleExecTransaction tests", () => {
]);
wallet.threshold = BigInt.fromI32(3);
wallet.currentNonce = zeroBigInt();
wallet.transactions = [];
wallet.save();

let call = changetype<ExecTransactionCall>(newMockCall());
Expand Down

0 comments on commit 93cf8ee

Please sign in to comment.