@lombard.finance/sdk 2.0.7 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +432 -1639
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/sdk/getNetworkFeeSignature/getNetworkFeeSignature.stories.tsx +2 -2
- package/src/sdk/index.ts +2 -0
- package/src/sdk/storeNetworkFeeSignature/storeNetworkFeeSignature.stories.tsx +3 -3
- package/src/sdk/storeStakeAndBakeSignature/index.ts +1 -0
- package/src/sdk/storeStakeAndBakeSignature/storeStakeAndBakeSignature.stories.tsx +185 -0
- package/src/sdk/storeStakeAndBakeSignature/storeStakeAndBakeSignature.ts +56 -0
- package/src/web3Sdk/getLBTCMintingFee/getLBTCMintingFee.stories.tsx +2 -2
- package/src/web3Sdk/index.ts +2 -11
- package/src/web3Sdk/signStakeAndBake/config.ts +13 -0
- package/src/web3Sdk/signStakeAndBake/contracts.ts +13 -0
- package/src/web3Sdk/signStakeAndBake/getTypedData.ts +78 -0
- package/src/web3Sdk/signStakeAndBake/index.ts +2 -0
- package/src/web3Sdk/signStakeAndBake/signStakeAndBake.stories.tsx +197 -0
- package/src/web3Sdk/signStakeAndBake/signStakeAndBake.ts +103 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { FormControl, InputLabel, MenuItem, Select, TextField } from '@mui/material';
|
|
2
|
+
import type { Meta } from '@storybook/react';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { OChainId } from '../../common/types/types';
|
|
5
|
+
import { Button } from '../../stories/components/Button';
|
|
6
|
+
import { CodeBlock } from '../../stories/components/CodeBlock';
|
|
7
|
+
import { useConnect } from '../../stories/hooks/useConnect';
|
|
8
|
+
import useQuery from '../../stories/hooks/useQuery';
|
|
9
|
+
import { VAULT_CONTRACTS } from '../../web3Sdk/signStakeAndBake/contracts';
|
|
10
|
+
import { signStakeAndBake } from '../../web3Sdk/signStakeAndBake/signStakeAndBake';
|
|
11
|
+
|
|
12
|
+
const EXPIRY_OPTIONS = {
|
|
13
|
+
'10 seconds': 10,
|
|
14
|
+
'1 minute': 60,
|
|
15
|
+
'1 hour': 3600,
|
|
16
|
+
'1 day': 86400,
|
|
17
|
+
'1 year': 31536000,
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
type ExpiryOption = keyof typeof EXPIRY_OPTIONS;
|
|
21
|
+
|
|
22
|
+
const AVAILABLE_CHAINS = Object.keys(VAULT_CONTRACTS).map(Number);
|
|
23
|
+
|
|
24
|
+
const meta = {
|
|
25
|
+
title: 'SDK/storeStakeAndBakeSignature',
|
|
26
|
+
component: StoryView,
|
|
27
|
+
tags: ['autodocs'],
|
|
28
|
+
} satisfies Meta<typeof StoryView>;
|
|
29
|
+
|
|
30
|
+
export default meta;
|
|
31
|
+
|
|
32
|
+
export function StoryView() {
|
|
33
|
+
const [selectedExpiry, setSelectedExpiry] =
|
|
34
|
+
useState<ExpiryOption>('10 seconds');
|
|
35
|
+
const [selectedChain, setSelectedChain] = useState(OChainId.holesky);
|
|
36
|
+
const [spender, setSpender] = useState(VAULT_CONTRACTS[OChainId.holesky].SPENDER);
|
|
37
|
+
const [verifyingContract, setVerifyingContract] = useState(
|
|
38
|
+
VAULT_CONTRACTS[OChainId.holesky].VERIFYING_CONTRACT,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
data: connectData,
|
|
43
|
+
error: connectError,
|
|
44
|
+
isLoading: isConnectLoading,
|
|
45
|
+
connect,
|
|
46
|
+
} = useConnect();
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const contracts = VAULT_CONTRACTS[selectedChain];
|
|
50
|
+
setSpender(contracts.SPENDER);
|
|
51
|
+
setVerifyingContract(contracts.VERIFYING_CONTRACT);
|
|
52
|
+
}, [selectedChain]);
|
|
53
|
+
|
|
54
|
+
const request = async () => {
|
|
55
|
+
if (!connectData || !connectData.provider) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const expiry =
|
|
60
|
+
Math.floor(Date.now() / 1000) + EXPIRY_OPTIONS[selectedExpiry];
|
|
61
|
+
|
|
62
|
+
return signStakeAndBake({
|
|
63
|
+
provider: connectData.provider,
|
|
64
|
+
address: connectData.account,
|
|
65
|
+
chainId: selectedChain,
|
|
66
|
+
value: '1999',
|
|
67
|
+
expiry,
|
|
68
|
+
spender,
|
|
69
|
+
verifyingContract,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const { data, error, isLoading, refetch } = useQuery(
|
|
74
|
+
request,
|
|
75
|
+
[selectedExpiry, selectedChain, spender, verifyingContract],
|
|
76
|
+
false,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const formattedConnectData = connectData && {
|
|
80
|
+
account: connectData.account,
|
|
81
|
+
chainId: connectData.chainId,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<>
|
|
86
|
+
<p>
|
|
87
|
+
This method stores the stake and bake signature in the backend. The
|
|
88
|
+
signature is used to approve spending of tokens.
|
|
89
|
+
</p>
|
|
90
|
+
|
|
91
|
+
<div className="mb-4">
|
|
92
|
+
<Button
|
|
93
|
+
onClick={connect}
|
|
94
|
+
disabled={isConnectLoading}
|
|
95
|
+
isLoading={isConnectLoading}
|
|
96
|
+
>
|
|
97
|
+
Connect
|
|
98
|
+
</Button>
|
|
99
|
+
|
|
100
|
+
<CodeBlock text={connectError || formattedConnectData} />
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div className="mb-4">
|
|
104
|
+
<FormControl fullWidth>
|
|
105
|
+
<InputLabel id="chain-select-label">Chain</InputLabel>
|
|
106
|
+
<Select
|
|
107
|
+
labelId="chain-select-label"
|
|
108
|
+
value={selectedChain}
|
|
109
|
+
label="Chain"
|
|
110
|
+
onChange={e =>
|
|
111
|
+
setSelectedChain(Number(e.target.value) as typeof selectedChain)
|
|
112
|
+
}
|
|
113
|
+
>
|
|
114
|
+
{AVAILABLE_CHAINS.map(chainId => (
|
|
115
|
+
<MenuItem key={chainId} value={chainId}>
|
|
116
|
+
{chainId} Holesky
|
|
117
|
+
</MenuItem>
|
|
118
|
+
))}
|
|
119
|
+
</Select>
|
|
120
|
+
</FormControl>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div className="mb-4">
|
|
124
|
+
<FormControl fullWidth>
|
|
125
|
+
<TextField
|
|
126
|
+
label="Spender Address"
|
|
127
|
+
value={spender}
|
|
128
|
+
onChange={e => setSpender(e.target.value)}
|
|
129
|
+
helperText="The address that will be authorized to spend tokens"
|
|
130
|
+
/>
|
|
131
|
+
</FormControl>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div className="mb-4">
|
|
135
|
+
<FormControl fullWidth>
|
|
136
|
+
<TextField
|
|
137
|
+
label="Verifying Contract"
|
|
138
|
+
value={verifyingContract}
|
|
139
|
+
onChange={e => setVerifyingContract(e.target.value)}
|
|
140
|
+
helperText="The contract that will verify the signature"
|
|
141
|
+
/>
|
|
142
|
+
</FormControl>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div className="mb-4">
|
|
146
|
+
<FormControl fullWidth>
|
|
147
|
+
<InputLabel id="expiry-select-label">Expiry Time</InputLabel>
|
|
148
|
+
<Select
|
|
149
|
+
labelId="expiry-select-label"
|
|
150
|
+
value={selectedExpiry}
|
|
151
|
+
label="Expiry Time"
|
|
152
|
+
onChange={e => setSelectedExpiry(e.target.value as ExpiryOption)}
|
|
153
|
+
>
|
|
154
|
+
{Object.keys(EXPIRY_OPTIONS).map(option => (
|
|
155
|
+
<MenuItem key={option} value={option}>
|
|
156
|
+
{option}
|
|
157
|
+
</MenuItem>
|
|
158
|
+
))}
|
|
159
|
+
</Select>
|
|
160
|
+
</FormControl>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<Button
|
|
164
|
+
onClick={refetch}
|
|
165
|
+
disabled={
|
|
166
|
+
isLoading || !connectData || connectData.chainId !== selectedChain
|
|
167
|
+
}
|
|
168
|
+
isLoading={isLoading}
|
|
169
|
+
>
|
|
170
|
+
Store Stake and Bake Signature
|
|
171
|
+
</Button>
|
|
172
|
+
|
|
173
|
+
<CodeBlock
|
|
174
|
+
text={
|
|
175
|
+
error ||
|
|
176
|
+
(data && {
|
|
177
|
+
...data,
|
|
178
|
+
signature: data.signature,
|
|
179
|
+
typedData: data.typedData ? JSON.parse(data.typedData) : '',
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
/>
|
|
183
|
+
</>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { IEnvParam } from '../../common/types/internalTypes';
|
|
3
|
+
import { getErrorMessage } from '../../common/utils/getErrorMessage';
|
|
4
|
+
import { getApiConfig } from '../apiConfig';
|
|
5
|
+
|
|
6
|
+
export type IStoreStakeAndBakeSignatureStatus = 'success';
|
|
7
|
+
|
|
8
|
+
interface IStoreStakeAndBakeSignatureResponse {
|
|
9
|
+
status: IStoreStakeAndBakeSignatureStatus;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IStoreStakeAndBakeSignatureParams extends IEnvParam {
|
|
13
|
+
/**
|
|
14
|
+
* signature
|
|
15
|
+
*/
|
|
16
|
+
signature: string;
|
|
17
|
+
/**
|
|
18
|
+
* JSON typed data used for the signature
|
|
19
|
+
*/
|
|
20
|
+
typedData: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Store stake and bake signature
|
|
25
|
+
*
|
|
26
|
+
* @param {IStoreStakeAndBakeSignatureParams} params - The parameters for storing stake and bake signature
|
|
27
|
+
*
|
|
28
|
+
* @returns {Promise<IStoreStakeAndBakeSignatureStatus>} Response promise with status
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
export async function storeStakeAndBakeSignature({
|
|
32
|
+
signature,
|
|
33
|
+
typedData,
|
|
34
|
+
env,
|
|
35
|
+
}: IStoreStakeAndBakeSignatureParams): Promise<IStoreStakeAndBakeSignatureStatus> {
|
|
36
|
+
const { baseApiUrl } = getApiConfig(env);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const { data } = await axios.post<IStoreStakeAndBakeSignatureResponse>(
|
|
40
|
+
`${baseApiUrl}/api/v1/claimer/save-stake-and-bake-signature`,
|
|
41
|
+
null,
|
|
42
|
+
{
|
|
43
|
+
params: {
|
|
44
|
+
typed_data: typedData,
|
|
45
|
+
signature,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return data.status;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
const errorMsg = getErrorMessage(error);
|
|
53
|
+
|
|
54
|
+
throw new Error(errorMsg);
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/web3Sdk/index.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
export * from '../sdk/getNetworkFeeSignature';
|
|
2
|
-
export * from '../sdk/storeNetworkFeeSignature';
|
|
3
|
-
export * from './approveLBTC';
|
|
4
|
-
export * from './claimLBTC';
|
|
5
|
-
export * from './getBasculeDepositStatus';
|
|
6
1
|
export * from './getLBTCMintingFee';
|
|
7
|
-
export * from './getLBTCTotalSupply';
|
|
8
|
-
export * from './getPermitNonce';
|
|
9
|
-
export * from './lbtcAddressConfig';
|
|
10
|
-
export * from './lbtcOFTAdapterAddressConfig';
|
|
11
2
|
export * from './signLbtcDestionationAddr';
|
|
12
3
|
export * from './signNetworkFee';
|
|
13
|
-
export * from './
|
|
14
|
-
export * from './
|
|
4
|
+
export * from './signStakeAndBake';
|
|
5
|
+
export * from './signStakeAndBake/config';
|
|
15
6
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { OChainId, TChainId } from '../../common/types/types';
|
|
2
|
+
|
|
3
|
+
export const STAKE_AND_BAKE_SPENDER_ADDRESSES: Record<number, string> = {
|
|
4
|
+
[OChainId.holesky]: '0x52BD640617eeD47A00dA0da93351092D49208d1d',
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const getStakeAndBakeSpenderAddress = (chainId: TChainId): string => {
|
|
8
|
+
const address = STAKE_AND_BAKE_SPENDER_ADDRESSES[chainId];
|
|
9
|
+
if (!address) {
|
|
10
|
+
throw new Error(`No spender address configured for chain ID ${chainId}`);
|
|
11
|
+
}
|
|
12
|
+
return address;
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { OChainId } from '../../common/types/types';
|
|
2
|
+
|
|
3
|
+
interface IVaultContracts {
|
|
4
|
+
SPENDER: string;
|
|
5
|
+
VERIFYING_CONTRACT: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const VAULT_CONTRACTS: Record<number, IVaultContracts> = {
|
|
9
|
+
[OChainId.holesky]: {
|
|
10
|
+
SPENDER: '0x52BD640617eeD47A00dA0da93351092D49208d1d',
|
|
11
|
+
VERIFYING_CONTRACT: '0xED7bfd5C1790576105Af4649817f6d35A75CD818',
|
|
12
|
+
},
|
|
13
|
+
} as const;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { TChainId } from '../../common/types/types';
|
|
2
|
+
import { getPermitNonce } from '../getPermitNonce';
|
|
3
|
+
|
|
4
|
+
export interface IStakeAndBakeTypedData {
|
|
5
|
+
chainId: TChainId;
|
|
6
|
+
expiry: number;
|
|
7
|
+
owner: string;
|
|
8
|
+
spender: string;
|
|
9
|
+
value: string;
|
|
10
|
+
rpcUrl?: string;
|
|
11
|
+
verifyingContract: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generates EIP-712 typed data for stake and bake signature
|
|
16
|
+
*
|
|
17
|
+
* @param {IStakeAndBakeTypedData} params - Parameters for generating typed data
|
|
18
|
+
* @returns {object} The typed data object conforming to EIP-712
|
|
19
|
+
*/
|
|
20
|
+
export async function getStakeAndBakeTypedData({
|
|
21
|
+
chainId,
|
|
22
|
+
expiry,
|
|
23
|
+
owner,
|
|
24
|
+
spender,
|
|
25
|
+
value,
|
|
26
|
+
rpcUrl,
|
|
27
|
+
verifyingContract,
|
|
28
|
+
}: IStakeAndBakeTypedData) {
|
|
29
|
+
const nonce = await getPermitNonce({
|
|
30
|
+
owner,
|
|
31
|
+
chainId,
|
|
32
|
+
rpcUrl,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
domain: {
|
|
37
|
+
name: 'Lombard Staked Bitcoin',
|
|
38
|
+
version: '1',
|
|
39
|
+
chainId,
|
|
40
|
+
verifyingContract,
|
|
41
|
+
},
|
|
42
|
+
types: {
|
|
43
|
+
EIP712Domain: [
|
|
44
|
+
{
|
|
45
|
+
name: 'name',
|
|
46
|
+
type: 'string',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'version',
|
|
50
|
+
type: 'string',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'chainId',
|
|
54
|
+
type: 'uint256',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'verifyingContract',
|
|
58
|
+
type: 'address',
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
Permit: [
|
|
62
|
+
{ name: 'owner', type: 'address' },
|
|
63
|
+
{ name: 'spender', type: 'address' },
|
|
64
|
+
{ name: 'value', type: 'uint256' },
|
|
65
|
+
{ name: 'nonce', type: 'uint256' },
|
|
66
|
+
{ name: 'deadline', type: 'uint256' },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
primaryType: 'Permit',
|
|
70
|
+
message: {
|
|
71
|
+
owner,
|
|
72
|
+
spender,
|
|
73
|
+
value,
|
|
74
|
+
nonce,
|
|
75
|
+
deadline: expiry.toString(),
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FormControl,
|
|
3
|
+
InputLabel,
|
|
4
|
+
MenuItem,
|
|
5
|
+
Select,
|
|
6
|
+
TextField,
|
|
7
|
+
} from '@mui/material';
|
|
8
|
+
import type { Meta } from '@storybook/react';
|
|
9
|
+
import { useEffect, useState } from 'react';
|
|
10
|
+
import { OChainId } from '../../common/types/types';
|
|
11
|
+
import { Button } from '../../stories/components/Button';
|
|
12
|
+
import { CodeBlock } from '../../stories/components/CodeBlock';
|
|
13
|
+
import { useConnect } from '../../stories/hooks/useConnect';
|
|
14
|
+
import useQuery from '../../stories/hooks/useQuery';
|
|
15
|
+
import { fromCamelCase } from '../../stories/utils/fromCamelCase';
|
|
16
|
+
import { VAULT_CONTRACTS } from './contracts';
|
|
17
|
+
import { signStakeAndBake } from './signStakeAndBake';
|
|
18
|
+
|
|
19
|
+
const { name } = signStakeAndBake;
|
|
20
|
+
const nameWithWhitespaces = fromCamelCase(name);
|
|
21
|
+
|
|
22
|
+
const EXPIRY_OPTIONS = {
|
|
23
|
+
'10 seconds': 10,
|
|
24
|
+
'1 minute': 60,
|
|
25
|
+
'1 hour': 3600,
|
|
26
|
+
'1 day': 86400,
|
|
27
|
+
'1 year': 31536000,
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
type ExpiryOption = keyof typeof EXPIRY_OPTIONS;
|
|
31
|
+
|
|
32
|
+
const AVAILABLE_CHAINS = Object.keys(VAULT_CONTRACTS).map(Number);
|
|
33
|
+
|
|
34
|
+
const meta = {
|
|
35
|
+
title: 'Web3SDK/signStakeAndBake',
|
|
36
|
+
component: StoryView,
|
|
37
|
+
tags: ['autodocs'],
|
|
38
|
+
} satisfies Meta<typeof StoryView>;
|
|
39
|
+
|
|
40
|
+
export default meta;
|
|
41
|
+
|
|
42
|
+
export function StoryView() {
|
|
43
|
+
const [selectedExpiry, setSelectedExpiry] =
|
|
44
|
+
useState<ExpiryOption>('10 seconds');
|
|
45
|
+
const [selectedChain, setSelectedChain] = useState(OChainId.holesky);
|
|
46
|
+
const [spender, setSpender] = useState(
|
|
47
|
+
VAULT_CONTRACTS[OChainId.holesky].SPENDER,
|
|
48
|
+
);
|
|
49
|
+
const [verifyingContract, setVerifyingContract] = useState(
|
|
50
|
+
VAULT_CONTRACTS[OChainId.holesky].VERIFYING_CONTRACT,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const {
|
|
54
|
+
data: connectData,
|
|
55
|
+
error: connectError,
|
|
56
|
+
isLoading: isConnectLoading,
|
|
57
|
+
connect,
|
|
58
|
+
} = useConnect();
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const contracts = VAULT_CONTRACTS[selectedChain];
|
|
62
|
+
setSpender(contracts.SPENDER);
|
|
63
|
+
setVerifyingContract(contracts.VERIFYING_CONTRACT);
|
|
64
|
+
}, [selectedChain]);
|
|
65
|
+
|
|
66
|
+
const request = async () => {
|
|
67
|
+
if (!connectData || !connectData.provider) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const expiry =
|
|
72
|
+
Math.floor(Date.now() / 1000) + EXPIRY_OPTIONS[selectedExpiry];
|
|
73
|
+
|
|
74
|
+
return signStakeAndBake({
|
|
75
|
+
provider: connectData.provider,
|
|
76
|
+
address: connectData.account,
|
|
77
|
+
chainId: selectedChain,
|
|
78
|
+
value: '1999',
|
|
79
|
+
expiry,
|
|
80
|
+
spender,
|
|
81
|
+
verifyingContract,
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const { data, error, isLoading, refetch } = useQuery(
|
|
86
|
+
request,
|
|
87
|
+
[selectedExpiry, selectedChain, spender, verifyingContract],
|
|
88
|
+
false,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const formattedConnectData = connectData && {
|
|
92
|
+
account: connectData.account,
|
|
93
|
+
chainId: connectData.chainId,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<>
|
|
98
|
+
<p>
|
|
99
|
+
This method is used to sign a permit for stake and bake operations. The
|
|
100
|
+
signature is used to approve spending of tokens.
|
|
101
|
+
</p>
|
|
102
|
+
|
|
103
|
+
<div className="mb-4">
|
|
104
|
+
<Button
|
|
105
|
+
onClick={connect}
|
|
106
|
+
disabled={isConnectLoading}
|
|
107
|
+
isLoading={isConnectLoading}
|
|
108
|
+
>
|
|
109
|
+
Connect
|
|
110
|
+
</Button>
|
|
111
|
+
|
|
112
|
+
<CodeBlock text={connectError || formattedConnectData} />
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div className="mb-4">
|
|
116
|
+
<FormControl fullWidth>
|
|
117
|
+
<InputLabel id="chain-select-label">Chain</InputLabel>
|
|
118
|
+
<Select
|
|
119
|
+
labelId="chain-select-label"
|
|
120
|
+
value={selectedChain}
|
|
121
|
+
label="Chain"
|
|
122
|
+
onChange={e =>
|
|
123
|
+
setSelectedChain(Number(e.target.value) as typeof selectedChain)
|
|
124
|
+
}
|
|
125
|
+
>
|
|
126
|
+
{AVAILABLE_CHAINS.map(chainId => (
|
|
127
|
+
<MenuItem key={chainId} value={chainId}>
|
|
128
|
+
{chainId}
|
|
129
|
+
</MenuItem>
|
|
130
|
+
))}
|
|
131
|
+
</Select>
|
|
132
|
+
</FormControl>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div className="mb-4">
|
|
136
|
+
<FormControl fullWidth>
|
|
137
|
+
<TextField
|
|
138
|
+
label="Spender Address"
|
|
139
|
+
value={spender}
|
|
140
|
+
onChange={e => setSpender(e.target.value)}
|
|
141
|
+
helperText="The address that will be authorized to spend tokens"
|
|
142
|
+
/>
|
|
143
|
+
</FormControl>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div className="mb-4">
|
|
147
|
+
<FormControl fullWidth>
|
|
148
|
+
<TextField
|
|
149
|
+
label="Verifying Contract"
|
|
150
|
+
value={verifyingContract}
|
|
151
|
+
onChange={e => setVerifyingContract(e.target.value)}
|
|
152
|
+
helperText="The contract that will verify the signature"
|
|
153
|
+
/>
|
|
154
|
+
</FormControl>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div className="mb-4">
|
|
158
|
+
<FormControl fullWidth>
|
|
159
|
+
<InputLabel id="expiry-select-label">Expiry Time</InputLabel>
|
|
160
|
+
<Select
|
|
161
|
+
labelId="expiry-select-label"
|
|
162
|
+
value={selectedExpiry}
|
|
163
|
+
label="Expiry Time"
|
|
164
|
+
onChange={e => setSelectedExpiry(e.target.value as ExpiryOption)}
|
|
165
|
+
>
|
|
166
|
+
{Object.keys(EXPIRY_OPTIONS).map(option => (
|
|
167
|
+
<MenuItem key={option} value={option}>
|
|
168
|
+
{option}
|
|
169
|
+
</MenuItem>
|
|
170
|
+
))}
|
|
171
|
+
</Select>
|
|
172
|
+
</FormControl>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<Button
|
|
176
|
+
onClick={refetch}
|
|
177
|
+
disabled={
|
|
178
|
+
isLoading || !connectData || connectData.chainId !== selectedChain
|
|
179
|
+
}
|
|
180
|
+
isLoading={isLoading}
|
|
181
|
+
>
|
|
182
|
+
{nameWithWhitespaces}
|
|
183
|
+
</Button>
|
|
184
|
+
|
|
185
|
+
<CodeBlock
|
|
186
|
+
text={
|
|
187
|
+
error ||
|
|
188
|
+
(data && {
|
|
189
|
+
...data,
|
|
190
|
+
signature: data.signature,
|
|
191
|
+
typedData: data.typedData ? JSON.parse(data.typedData) : '',
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
/>
|
|
195
|
+
</>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { TChainId } from '../../common/types/types';
|
|
2
|
+
import { Provider } from '../../provider';
|
|
3
|
+
import { IProviderBasedParams } from '../types';
|
|
4
|
+
import { getStakeAndBakeTypedData } from './getTypedData';
|
|
5
|
+
|
|
6
|
+
const NO_SIGNATURE_ERROR =
|
|
7
|
+
'Failed to obtain a valid signature. The response is undefined or invalid.';
|
|
8
|
+
|
|
9
|
+
export interface ISignStakeAndBakeParams
|
|
10
|
+
extends Pick<IProviderBasedParams, 'provider'> {
|
|
11
|
+
/**
|
|
12
|
+
* The address to sign with (owner)
|
|
13
|
+
*/
|
|
14
|
+
address: string;
|
|
15
|
+
/**
|
|
16
|
+
* Chain ID for the signature
|
|
17
|
+
*/
|
|
18
|
+
chainId: TChainId;
|
|
19
|
+
/**
|
|
20
|
+
* The value to approve
|
|
21
|
+
*/
|
|
22
|
+
value: string;
|
|
23
|
+
/**
|
|
24
|
+
* Expiry date as a unix timestamp
|
|
25
|
+
*/
|
|
26
|
+
expiry: number;
|
|
27
|
+
/**
|
|
28
|
+
* Optional RPC URL for the network
|
|
29
|
+
*/
|
|
30
|
+
rpcUrl?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The spender address that will be authorized to spend tokens
|
|
33
|
+
*/
|
|
34
|
+
spender: string;
|
|
35
|
+
/**
|
|
36
|
+
* The contract address that will verify the signature
|
|
37
|
+
*/
|
|
38
|
+
verifyingContract: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ISignStakeAndBakeResult {
|
|
42
|
+
/**
|
|
43
|
+
* The signature
|
|
44
|
+
*/
|
|
45
|
+
signature: string;
|
|
46
|
+
/**
|
|
47
|
+
* The typed data used to generate the signature
|
|
48
|
+
*/
|
|
49
|
+
typedData: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Signs stake and bake authorization with EIP-712
|
|
54
|
+
*
|
|
55
|
+
* @param {ISignStakeAndBakeParams} params - Parameters for signing
|
|
56
|
+
* @returns {Promise<ISignStakeAndBakeResult>} The signature and typed data
|
|
57
|
+
*/
|
|
58
|
+
export async function signStakeAndBake({
|
|
59
|
+
address,
|
|
60
|
+
provider,
|
|
61
|
+
chainId,
|
|
62
|
+
value,
|
|
63
|
+
expiry,
|
|
64
|
+
rpcUrl,
|
|
65
|
+
spender,
|
|
66
|
+
verifyingContract,
|
|
67
|
+
}: ISignStakeAndBakeParams): Promise<ISignStakeAndBakeResult> {
|
|
68
|
+
const providerInstance = new Provider({
|
|
69
|
+
provider,
|
|
70
|
+
account: address,
|
|
71
|
+
chainId,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const typedDataObject = await getStakeAndBakeTypedData({
|
|
75
|
+
chainId,
|
|
76
|
+
expiry,
|
|
77
|
+
owner: address,
|
|
78
|
+
spender,
|
|
79
|
+
value,
|
|
80
|
+
rpcUrl,
|
|
81
|
+
verifyingContract,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const typedData = JSON.stringify(typedDataObject);
|
|
85
|
+
|
|
86
|
+
const signature = await providerInstance.web3?.currentProvider?.request<
|
|
87
|
+
'eth_signTypedData_v4',
|
|
88
|
+
string
|
|
89
|
+
>({
|
|
90
|
+
method: 'eth_signTypedData_v4',
|
|
91
|
+
params: [address, typedData],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (typeof signature === 'string') {
|
|
95
|
+
return { signature, typedData };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!signature?.result) {
|
|
99
|
+
throw new Error(NO_SIGNATURE_ERROR);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { signature: signature.result, typedData };
|
|
103
|
+
}
|