-
Notifications
You must be signed in to change notification settings - Fork 271
Expand file tree
/
Copy pathsdk.ts
More file actions
158 lines (136 loc) · 5.46 KB
/
sdk.ts
File metadata and controls
158 lines (136 loc) · 5.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import type { TransactionReceipt, TransactionRequest } from '@ethersproject/abstract-provider'
import type { Signer } from '@ethersproject/abstract-signer'
import Safe, { ConnectSafeConfig, EthersAdapter } from '@safe-global/protocol-kit'
import SafeApiKit from '@safe-global/api-kit'
import { MetaTransactionData, OperationType, SafeTransaction } from '@safe-global/safe-core-sdk-types'
import type { EndpointId } from '@layerzerolabs/lz-definitions'
import {
OmniSignerBase,
type OmniTransactionResponse,
type OmniSigner,
type OmniTransaction,
type OmniPoint,
} from '@layerzerolabs/devtools'
import { ethers } from 'ethers'
export abstract class OmniSignerEVMBase extends OmniSignerBase implements OmniSigner {
protected constructor(
eid: EndpointId,
public readonly signer: Signer
) {
super(eid)
}
async getPoint(): Promise<OmniPoint> {
return { eid: this.eid, address: await this.signer.getAddress() }
}
}
/**
* Implements an OmniSigner interface for EVM-compatible chains
*/
export class OmniSignerEVM extends OmniSignerEVMBase {
constructor(eid: EndpointId, signer: Signer) {
super(eid, signer)
}
async sign(transaction: OmniTransaction): Promise<string> {
this.assertTransaction(transaction)
return this.signer.signTransaction(this.#serializeTransaction(transaction))
}
async signAndSend(transaction: OmniTransaction): Promise<OmniTransactionResponse<TransactionReceipt>> {
this.assertTransaction(transaction)
const nativeTransaction = this.#serializeTransaction(transaction)
const { hash, ...response } = await this.signer.sendTransaction(nativeTransaction)
return {
...response,
transactionHash: hash,
}
}
#serializeTransaction(transaction: OmniTransaction): TransactionRequest {
return {
// mandatory
to: transaction.point.address,
data: transaction.data,
// optional
...(transaction.gasLimit != null && { gasLimit: transaction.gasLimit }),
...(transaction.value != null && { value: transaction.value }),
}
}
}
/**
* Implements an OmniSigner interface for EVM-compatible chains using Gnosis Safe.
*/
export class GnosisOmniSignerEVM<
TSafeConfig extends ConnectSafeConfig & { safeApiKey: string },
> extends OmniSignerEVMBase {
constructor(
eid: EndpointId,
signer: Signer,
protected readonly safeUrl: string,
protected readonly safeConfig: TSafeConfig,
protected readonly chainId: bigint,
protected readonly ethAdapter = new EthersAdapter({
ethers,
signerOrProvider: signer,
}),
protected readonly apiKit = new SafeApiKit({ txServiceUrl: safeUrl, ethAdapter }),
protected readonly safeSdkPromise: Safe | Promise<Safe> = Safe.create({
ethAdapter,
safeAddress: safeConfig.safeAddress!,
contractNetworks: safeConfig.contractNetworks,
})
) {
super(eid, signer)
}
async sign(_: OmniTransaction): Promise<string> {
throw new Error(`Signing transactions with safe is currently not supported, use signAndSend instead`)
}
async signAndSend(transaction: OmniTransaction): Promise<OmniTransactionResponse> {
return this.signAndSendBatch([transaction])
}
async signAndSendBatch(transactions: OmniTransaction[]): Promise<OmniTransactionResponse> {
if (transactions.length === 0) {
throw new Error('/signAndSendBatch received 0 transactions')
}
const safeTransaction = await this.#createSafeTransaction(transactions)
return this.#proposeSafeTransaction(safeTransaction)
}
async #proposeSafeTransaction(safeTransaction: SafeTransaction): Promise<OmniTransactionResponse> {
const safeSdk = await this.safeSdkPromise
const safeAddress = await safeSdk.getAddress()
const safeTxHash = await safeSdk.getTransactionHash(safeTransaction)
const senderSignature = await safeSdk.signTransactionHash(safeTxHash)
const senderAddress = await this.signer.getAddress()
await this.apiKit.proposeTransaction({
senderSignature: senderSignature.data,
safeAddress,
safeTransactionData: safeTransaction.data,
safeTxHash,
senderAddress,
})
return {
transactionHash: safeTxHash,
wait: async (_confirmations?: number) => {
return {
transactionHash: safeTxHash,
}
},
}
}
async #createSafeTransaction(transactions: OmniTransaction[]): Promise<SafeTransaction> {
transactions.forEach((transaction) => this.assertTransaction(transaction))
const safeSdk = await this.safeSdkPromise
const safeAddress = await safeSdk.getAddress()
const nonce = await this.apiKit.getNextNonce(safeAddress)
return safeSdk.createTransaction({
safeTransactionData: transactions.map((transaction) => this.#serializeTransaction(transaction)),
options: { nonce },
onlyCalls: true,
})
}
#serializeTransaction(transaction: OmniTransaction): MetaTransactionData {
return {
to: transaction.point.address,
data: transaction.data,
value: String(transaction.value ?? 0),
operation: OperationType.Call,
}
}
}