Skip to content

Commit

Permalink
Add EIP-5792 support to Safe Apps Provider (#605)
Browse files Browse the repository at this point in the history
* Add EIP-5792 support to Safe Apps Provider

* Add `wallet_sendCalls` support

* Improve error messages
  • Loading branch information
iamacook authored Nov 6, 2024
1 parent 5f9ef70 commit 21ffde5
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/wicked-coins-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@safe-global/safe-apps-provider': patch
---

Add EIP-5792 support
73 changes: 72 additions & 1 deletion packages/safe-apps-provider/dist/provider.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/safe-apps-provider/dist/provider.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/safe-apps-provider/dist/utils.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export declare function getLowerCase(value: string): string;
export declare function numberToHex(value: number): `0x${string}`;
6 changes: 5 additions & 1 deletion packages/safe-apps-provider/dist/utils.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/safe-apps-provider/dist/utils.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 93 additions & 3 deletions packages/safe-apps-provider/src/provider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SafeAppsSDK, { SafeInfo, Web3TransactionObject } from '@safe-global/safe-apps-sdk';
import SafeAppsSDK, { SafeInfo, TransactionStatus, Web3TransactionObject } from '@safe-global/safe-apps-sdk';
import { EventEmitter } from 'events';
import { EIP1193Provider } from './types';
import { getLowerCase } from './utils';
import { getLowerCase, numberToHex } from './utils';

// The API is based on Ethereum JavaScript API Provider Standard. Link: https://eips.ethereum.org/EIPS/eip-1193
export class SafeAppProvider extends EventEmitter implements EIP1193Provider {
Expand Down Expand Up @@ -38,7 +38,7 @@ export class SafeAppProvider extends EventEmitter implements EIP1193Provider {

case 'net_version':
case 'eth_chainId':
return `0x${this.chainId.toString(16)}`;
return numberToHex(this.chainId);

case 'personal_sign': {
const [message, address] = params;
Expand Down Expand Up @@ -195,6 +195,96 @@ export class SafeAppProvider extends EventEmitter implements EIP1193Provider {
case 'safe_setSettings':
return this.sdk.eth.setSafeSettings([params[0]]);

case 'wallet_sendCalls': {
if (params[0].from !== this.safe.safeAddress) {
throw Error('Invalid from address');
}

const txs = params[0].calls.map(
(
call: { to?: `0x${string}`; data?: `0x${string}`; value?: `0x${string}`; chainId?: `0x${string}` },
i: number,
) => {
if (call.chainId !== numberToHex(this.chainId)) {
throw new Error(`Invalid call #${i}: Safe is not on chain ${call.chainId}`);
}
if (!call.to) {
throw new Error(`Invalid call #${i}: missing "to" field`);
}
return {
to: call.to,
data: call.data ?? '0x',
value: call.value ?? numberToHex(0),
};
},
);

const { safeTxHash } = await this.sdk.txs.send({ txs });
return safeTxHash;
}

case 'wallet_getCallsStatus': {
const CallStatus: {
[key in TransactionStatus]: 'PENDING' | 'CONFIRMED';
} = {
[TransactionStatus.AWAITING_CONFIRMATIONS]: 'PENDING',
[TransactionStatus.AWAITING_EXECUTION]: 'PENDING',
[TransactionStatus.CANCELLED]: 'CONFIRMED',
[TransactionStatus.FAILED]: 'CONFIRMED',
[TransactionStatus.SUCCESS]: 'CONFIRMED',
};

const tx = await this.sdk.txs.getBySafeTxHash(params[0]).catch(() => null);
if (!tx?.txHash) {
throw new Error('Transaction not found');
}

const receipt = await this.sdk.eth.getTransactionReceipt([tx.txHash]).catch(() => null);
if (!receipt) {
throw new Error('Transaction receipt not found');
}

const calls =
tx.txData?.dataDecoded?.method !== 'multiSend'
? 1
: // Number of batched transactions
tx.txData.dataDecoded.parameters?.[0].valueDecoded?.length ?? 1;

// Typed as number; is hex
const blockNumber = Number(receipt.blockNumber);
const gasUsed = Number(receipt.gasUsed);

const receipts = Array(calls).fill({
success: numberToHex(tx.txStatus === TransactionStatus.SUCCESS ? 1 : 0),
blockHash: receipt.blockHash,
blockNumber: numberToHex(blockNumber),
blockTimestamp: numberToHex(tx.executedAt ?? 0),
gasUsed: numberToHex(gasUsed),
transactionHash: tx.txHash,
logs: receipt.logs,
});

return {
status: CallStatus[tx.txStatus],
receipts,
};
}

case 'wallet_showCallsStatus': {
// Cannot open transaction details page via SDK
throw new Error(`"${request.method}" not supported`);
}

case 'wallet_getCapabilities': {
return {
[numberToHex(this.chainId)]: {
atomicBatch: {
supported: true,
},
},
};
}

default:
throw Error(`"${request.method}" not implemented`);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/safe-apps-provider/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export function getLowerCase(value: string): string {
}
return value;
}

export function numberToHex(value: number): `0x${string}` {
return `0x${value.toString(16)}`;
}

0 comments on commit 21ffde5

Please sign in to comment.