-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(examples): creating an order via smart-contract wallet
- Loading branch information
Showing
11 changed files
with
2,301 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,13 @@ | |
"version": "0.1.0", | ||
"private": true, | ||
"dependencies": { | ||
"@cowprotocol/cow-sdk": "^2.0.0-alpha.6", | ||
"@cowprotocol/cow-sdk": "^4.0.3", | ||
"@safe-global/api-kit": "^1.3.0", | ||
"@safe-global/protocol-kit": "^1.2.0", | ||
"@testing-library/jest-dom": "^5.16.5", | ||
"@testing-library/react": "^13.4.0", | ||
"@testing-library/user-event": "^13.5.0", | ||
"ethers": "^5.7.2", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-scripts": "5.0.1", | ||
|
@@ -31,6 +34,15 @@ | |
] | ||
}, | ||
"devDependencies": { | ||
"buffer": "npm:[email protected]", | ||
"assert": "npm:[email protected]", | ||
"http": "npm:stream-http@^3.2.0", | ||
"https": "npm:https-browserify@^1.0.0", | ||
"zlib": "npm:browserify-zlib@^0.2.0", | ||
"crypto": "npm:[email protected]", | ||
"stream": "npm:[email protected]", | ||
"url": "npm:[email protected]", | ||
"util": "npm:[email protected]", | ||
"@types/jest": "^29.5.0", | ||
"@types/node": "^18.15.10", | ||
"@types/react": "^18.0.30", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
examples/cra/src/pages/smartContractWallet/GPv2Settlement.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[ | ||
{ | ||
"inputs": [ | ||
{ | ||
"internalType": "bytes", | ||
"name": "orderUid", | ||
"type": "bytes" | ||
}, | ||
{ | ||
"internalType": "bool", | ||
"name": "signed", | ||
"type": "bool" | ||
} | ||
], | ||
"name": "setPreSignature", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import GPv2SettlementAbi from './GPv2Settlement.json' | ||
import {SupportedChainId} from '@cowprotocol/cow-sdk' | ||
|
||
export const SETTLEMENT_CONTRACT_ADDRESS = '0x9008D19f58AAbD9eD0D60971565AA8510560ab41' | ||
|
||
export const SETTLEMENT_CONTRACT_ABI = GPv2SettlementAbi | ||
|
||
export const SAFE_TRANSACTION_SERVICE_URL: Record<SupportedChainId, string> = { | ||
[SupportedChainId.MAINNET]: 'https://safe-transaction-mainnet.safe.global', | ||
[SupportedChainId.GNOSIS_CHAIN]: 'https://safe-transaction-gnosis-chain.safe.global', | ||
[SupportedChainId.GOERLI]: 'https://safe-transaction-goerli.safe.global', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import {FormEvent, useCallback, useEffect, useMemo, useState} from 'react' | ||
|
||
import { Contract } from '@ethersproject/contracts' | ||
import { OrderBookApi, OrderCreation, OrderKind, SigningScheme, UnsignedOrder } from '@cowprotocol/cow-sdk' | ||
|
||
import { JsonContent } from '../../components/jsonContent' | ||
import { ResultContent } from '../../components/resultContent' | ||
import { useWeb3Info } from '../../hooks/useWeb3Info' | ||
import { useCurrentChainId } from '../../hooks/useCurrentChainId' | ||
|
||
import { SETTLEMENT_CONTRACT_ABI, SETTLEMENT_CONTRACT_ADDRESS } from './const' | ||
import { useSafeSdkAndKit } from './useSafeSdkAndKit' | ||
|
||
const appData = '{"appCode":"CoW Swap-SafeApp","environment":"local","metadata":{"orderClass":{"orderClass":"limit"},"quote":{"slippageBips":"0"}},"version":"0.11.0"}' | ||
const appDataHash = '0x6bb009e9730f09d18011327b6a1e4b9df70a3eb4d49e7cb622f79caadac5751a' | ||
|
||
|
||
const orderBookApi = new OrderBookApi() | ||
const settlementContract = new Contract(SETTLEMENT_CONTRACT_ADDRESS, SETTLEMENT_CONTRACT_ABI) | ||
|
||
export function SmartContractWallet() { | ||
const {provider, account} = useWeb3Info() | ||
const chainId = useCurrentChainId() | ||
|
||
const [safeAddress, setSafeAddress] = useState<string | null>(null) | ||
const [input, setInput] = useState<UnsignedOrder | null>(null) | ||
const [output, setOutput] = useState<any>('') | ||
|
||
const {safeSdk, safeApiKit} = useSafeSdkAndKit(safeAddress, chainId, provider) | ||
|
||
const defaultOrder: UnsignedOrder | null = useMemo(() => { | ||
return safeAddress ? { | ||
receiver: safeAddress, | ||
buyAmount: '650942340000000000000', | ||
buyToken: '0x91056D4A53E1faa1A84306D4deAEc71085394bC8', | ||
sellAmount: '100000000000000000', | ||
sellToken: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', | ||
validTo: Math.round((Date.now() + 900_000) / 1000), | ||
appData: '0x', | ||
feeAmount: '0', | ||
kind: OrderKind.SELL, | ||
partiallyFillable: true, | ||
signingScheme: SigningScheme.PRESIGN, | ||
} : null | ||
}, [safeAddress]) | ||
|
||
useEffect(() => { | ||
orderBookApi.context.chainId = chainId | ||
}, [chainId]) | ||
|
||
|
||
const signOrder = useCallback( | ||
async (event: FormEvent) => { | ||
event.preventDefault() | ||
|
||
if (!safeAddress) { | ||
alert('Please, specify Safe address') | ||
return | ||
} | ||
|
||
if (!input || !safeSdk || !safeApiKit) return | ||
|
||
setOutput('Loading...') | ||
|
||
try { | ||
const orderCreation: OrderCreation = { | ||
...input, | ||
from: safeAddress, | ||
signingScheme: SigningScheme.PRESIGN, | ||
signature: safeAddress, | ||
appData, | ||
appDataHash, | ||
} | ||
|
||
// Send order to CoW Protocol order-book | ||
const orderId = await orderBookApi.sendOrder(orderCreation) | ||
|
||
const presignCallData = settlementContract.interface.encodeFunctionData('setPreSignature', [ | ||
orderId, | ||
true, | ||
]) | ||
|
||
const presignRawTx = { | ||
to: settlementContract.address, | ||
data: presignCallData, | ||
value: '0', | ||
} | ||
|
||
// Sending pre-signature transaction to settlement contract | ||
// In this example we are using the Safe SDK, but you can use any other smart-contract wallet | ||
const safeTx = await safeSdk.createTransaction({safeTransactionData: presignRawTx}) | ||
const signedSafeTx = await safeSdk.signTransaction(safeTx) | ||
const safeTxHash = await safeSdk.getTransactionHash(signedSafeTx) | ||
const senderSignature = signedSafeTx.signatures.get(account.toLowerCase())?.data || '' | ||
|
||
await safeApiKit.proposeTransaction({ | ||
safeAddress, | ||
safeTransactionData: signedSafeTx.data, | ||
safeTxHash, | ||
senderAddress: account, | ||
senderSignature, | ||
}) | ||
|
||
setOutput({ orderId, safeTxHash, senderSignature }) | ||
} catch (e: any) { | ||
setOutput(e.toString()) | ||
} | ||
}, | ||
[chainId, input, provider, setOutput, safeSdk, safeApiKit] | ||
) | ||
|
||
return ( | ||
<div> | ||
<div className="form"> | ||
<div> | ||
<h1>Safe address:</h1> | ||
<input type="text" | ||
style={{width: '600px'}} | ||
value={safeAddress || ''} | ||
onChange={e => setSafeAddress(e.target.value)}/> | ||
|
||
<h1>Order:</h1> | ||
<JsonContent defaultValue={defaultOrder} onChange={setInput}/> | ||
</div> | ||
|
||
<div> | ||
<button onClick={signOrder}>Sign and send order</button> | ||
</div> | ||
</div> | ||
|
||
<ResultContent data={output}/> | ||
</div> | ||
) | ||
} |
39 changes: 39 additions & 0 deletions
39
examples/cra/src/pages/smartContractWallet/useSafeSdkAndKit.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import {useEffect, useState} from 'react' | ||
import Safe, {EthersAdapter} from '@safe-global/protocol-kit' | ||
import {ethers} from 'ethers' | ||
import SafeApiKit from '@safe-global/api-kit' | ||
import {SAFE_TRANSACTION_SERVICE_URL} from './const' | ||
import {SupportedChainId} from '@cowprotocol/cow-sdk' | ||
import {Web3Provider} from '@ethersproject/providers' | ||
|
||
export function useSafeSdkAndKit(safeAddress: string | null, chainId: SupportedChainId, provider: Web3Provider) { | ||
const [safeSdk, setSafeSdk] = useState<Safe | null>(null) | ||
const [safeApiKit, setSafeApiKit] = useState<SafeApiKit | null>(null) | ||
|
||
useEffect(() => { | ||
const txServiceUrl = SAFE_TRANSACTION_SERVICE_URL[chainId] | ||
|
||
if (!safeAddress) return | ||
|
||
if (!txServiceUrl) { | ||
console.error('Unsupported chainId', chainId) | ||
return | ||
} | ||
|
||
const ethAdapter = new EthersAdapter({ | ||
ethers, | ||
signerOrProvider: provider.getSigner(0), | ||
}) | ||
|
||
const safeApiKit = new SafeApiKit({ | ||
txServiceUrl, | ||
ethAdapter | ||
}) | ||
|
||
setSafeApiKit(safeApiKit) | ||
|
||
Safe.create({ethAdapter, safeAddress}).then(setSafeSdk).catch(console.error) | ||
}, [chainId, provider, safeAddress]) | ||
|
||
return { safeSdk, safeApiKit } | ||
} |
Oops, something went wrong.