Skip to content

Commit 282968f

Browse files
zhongericgretzke
andauthored
feat(smart-wallet): Initial commit (#319)
Co-authored-by: Daniel Gretzke <daniel@gretzke.de>
1 parent e3482db commit 282968f

21 files changed

+898
-27
lines changed

sdks/smart-wallet-sdk/.eslintrc.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = {
2+
parser: '@typescript-eslint/parser',
3+
parserOptions: {
4+
ecmaVersion: 2020,
5+
sourceType: 'module',
6+
},
7+
extends: [
8+
'plugin:@typescript-eslint/recommended',
9+
'plugin:eslint-comments/recommended',
10+
'plugin:import/typescript',
11+
'prettier',
12+
],
13+
plugins: ['@typescript-eslint', 'import'],
14+
rules: {
15+
'@typescript-eslint/explicit-function-return-type': 'off',
16+
'@typescript-eslint/no-explicit-any': 'off',
17+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
18+
'import/order': ['error', { 'newlines-between': 'always', alphabetize: { order: 'asc' } }],
19+
},
20+
}

sdks/smart-wallet-sdk/.gitignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Dependencies
2+
node_modules/
3+
4+
# Build output
5+
dist/
6+
build/
7+
8+
# Generated files
9+
src/contracts/
10+
11+
# Coverage
12+
coverage/
13+
14+
# Cache
15+
.cache/
16+
.turbo/
17+
18+
# IDE
19+
.idea/
20+
.vscode/
21+
22+
# Logs
23+
*.log
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# OS
29+
.DS_Store

sdks/smart-wallet-sdk/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Uniswap Labs
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

sdks/smart-wallet-sdk/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Smart Wallet SDK
2+
3+
⚒️ An SDK for building applications with smart wallets on Uniswap
4+
5+
This SDK provides utilities for interacting with Uniswap protocols using smart wallets.
6+
7+
## Installation
8+
9+
```bash
10+
npm install @uniswap/smart-wallet-sdk
11+
```
12+
13+
or
14+
15+
```bash
16+
yarn add @uniswap/smart-wallet-sdk
17+
```
18+
19+
## Documentation
20+
21+
Coming soon...
22+
23+
## License
24+
25+
MIT

sdks/smart-wallet-sdk/abis/MinimalDelegation.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

sdks/smart-wallet-sdk/jest.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
rootDir: 'src',
6+
globals: {
7+
'ts-jest': {
8+
tsconfig: 'tsconfig.base.json',
9+
},
10+
},
11+
}

sdks/smart-wallet-sdk/package.json

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"name": "@uniswap/smart-wallet-sdk",
3+
"description": "⚒️ An SDK for building applications with smart wallets on Uniswap",
4+
"repository": "https://github.com/Uniswap/sdks.git",
5+
"keywords": [
6+
"uniswap",
7+
"ethereum",
8+
"smart-wallet"
9+
],
10+
"license": "MIT",
11+
"main": "./dist/cjs/src/index.js",
12+
"module": "./dist/esm/src/index.js",
13+
"types": "./dist/types/src/index.d.ts",
14+
"files": [
15+
"dist"
16+
],
17+
"engines": {
18+
"node": ">=14"
19+
},
20+
"scripts": {
21+
"clean": "rm -rf dist src/contracts",
22+
"build": "yarn clean && yarn build:cjs && yarn build:esm && yarn build:types",
23+
"build:cjs": "tsc -p tsconfig.cjs.json",
24+
"build:esm": "tsc -p tsconfig.esm.json",
25+
"build:types": "tsc -p tsconfig.types.json",
26+
"lint": "eslint src --ext .ts",
27+
"test": "jest"
28+
},
29+
"exports": {
30+
".": {
31+
"types": "./dist/types/src/index.d.ts",
32+
"import": "./dist/esm/src/index.js",
33+
"require": "./dist/cjs/src/index.js"
34+
}
35+
},
36+
"sideEffects": false,
37+
"dependencies": {
38+
"@uniswap/sdk-core": "^7.6.0",
39+
"viem": "^2.23.5"
40+
},
41+
"devDependencies": {
42+
"@types/jest": "^24.0.25",
43+
"@types/node": "^18.7.16",
44+
"eslint": "^7.8.0",
45+
"eslint-config-prettier": "^6.11.0",
46+
"eslint-plugin-eslint-comments": "^3.2.0",
47+
"eslint-plugin-functional": "^3.0.2",
48+
"eslint-plugin-import": "^2.22.0",
49+
"jest": "25.5.0",
50+
"prettier": "^2.4.1",
51+
"ts-jest": "^25.5.1",
52+
"ts-node": "^10.9.1",
53+
"tslib": "^2.3.0",
54+
"typescript": "npm:typescript@^5.6.2"
55+
},
56+
"prettier": {
57+
"printWidth": 120,
58+
"semi": false,
59+
"singleQuote": true
60+
},
61+
"publishConfig": {
62+
"access": "public",
63+
"provenance": true
64+
},
65+
"release": {
66+
"extends": "semantic-release-monorepo",
67+
"branches": [
68+
{
69+
"name": "main",
70+
"prerelease": false
71+
}
72+
],
73+
"plugins": [
74+
[
75+
"@semantic-release/commit-analyzer",
76+
{
77+
"preset": "angular",
78+
"releaseRules": "../../publishing/release-rules.cjs"
79+
}
80+
],
81+
"@semantic-release/release-notes-generator",
82+
"@semantic-release/npm",
83+
"@semantic-release/github",
84+
[
85+
"@semantic-release/exec",
86+
{
87+
"successCmd": "git restore yarn.lock && yarn",
88+
"failCmd": "git restore yarn.lock && yarn",
89+
"execCwd": "../.."
90+
}
91+
]
92+
]
93+
}
94+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { ChainId } from '@uniswap/sdk-core'
2+
3+
/**
4+
* The target address for self-calls is address(0)
5+
*/
6+
export const SELF_CALL_TARGET = "0x0000000000000000000000000000000000000000"
7+
8+
/**
9+
* Call types for smart wallet calls
10+
* Follows ERC-7579
11+
*/
12+
export enum ModeType {
13+
BATCHED_CALL = '0x0100000000000000000000000000000000000000000000000000000000000000',
14+
BATCHED_CALL_CAN_REVERT = '0x0101000000000000000000000000000000000000000000000000000000000000',
15+
BATCHED_CALL_SUPPORTS_OPDATA = '0x0100000000007821000100000000000000000000000000000000000000000000',
16+
BATCHED_CALL_SUPPORTS_OPDATA_AND_CAN_REVERT = '0x0101000000007821000100000000000000000000000000000000000000000000'
17+
}
18+
19+
const BATCHED_CALL_EXECUTION_DATA_ABI = [
20+
{
21+
type: 'tuple[]',
22+
components: [
23+
{ type: 'address', name: 'to' },
24+
{ type: 'uint256', name: 'value' },
25+
{ type: 'bytes', name: 'data' }
26+
]
27+
}
28+
]
29+
30+
const BATCHED_CALL_SUPPORTS_OPDATA_EXECUTION_DATA_ABI = [
31+
...BATCHED_CALL_EXECUTION_DATA_ABI,
32+
{ type: 'bytes', name: 'opData' }
33+
]
34+
35+
/**
36+
* ABI encoding for each mode type
37+
*/
38+
export const MODE_TYPE_ABI_PARAMETERS = {
39+
[ModeType.BATCHED_CALL]: BATCHED_CALL_EXECUTION_DATA_ABI,
40+
[ModeType.BATCHED_CALL_CAN_REVERT]: BATCHED_CALL_EXECUTION_DATA_ABI,
41+
[ModeType.BATCHED_CALL_SUPPORTS_OPDATA]: BATCHED_CALL_SUPPORTS_OPDATA_EXECUTION_DATA_ABI,
42+
[ModeType.BATCHED_CALL_SUPPORTS_OPDATA_AND_CAN_REVERT]: BATCHED_CALL_SUPPORTS_OPDATA_EXECUTION_DATA_ABI
43+
} as const
44+
45+
/**
46+
* Mapping of chainId to Smart Wallet contract addresses
47+
*/
48+
export const SMART_WALLET_ADDRESSES: { [chainId in ChainId]?: `0x${string}` } = {
49+
// Mainnet
50+
[ChainId.MAINNET]: '0x0000000000000000000000000000000000000000', // Placeholder - to be replaced
51+
// Optimism
52+
[ChainId.OPTIMISM]: '0x0000000000000000000000000000000000000000', // Placeholder - to be replaced
53+
// Polygon
54+
[ChainId.POLYGON]: '0x0000000000000000000000000000000000000000', // Placeholder - to be replaced
55+
// Arbitrum
56+
[ChainId.ARBITRUM_ONE]: '0x0000000000000000000000000000000000000000', // Placeholder - to be replaced
57+
// Base
58+
[ChainId.BASE]: '0x0000000000000000000000000000000000000000', // Placeholder - to be replaced
59+
}

sdks/smart-wallet-sdk/src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Smart Wallet SDK entry point
2+
export const VERSION = '0.1.0'
3+
4+
// Export all types
5+
export * from './types'
6+
7+
// Export constants
8+
export * from './constants'
9+
10+
// Export utilities
11+
export * from './utils'
12+
13+
// Export main class
14+
export * from './smartWallet'
15+
16+
// Export generated contracts (will be available after running typechain)
17+
// export * from './contracts'
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { ChainId } from '@uniswap/sdk-core'
2+
3+
import { ModeType, SMART_WALLET_ADDRESSES } from './constants';
4+
import { SmartWallet } from './smartWallet'
5+
import { Call } from './types'
6+
7+
const EXECUTE_SELECTOR = "0xe9ae5c53" as `0x${string}`
8+
9+
describe('SmartWallet', () => {
10+
describe('encodeExecute', () => {
11+
it('encodes batch calls correctly', () => {
12+
const calls: Call[] = [
13+
{
14+
to: '0x1111111111111111111111111111111111111111',
15+
data: '0x1234',
16+
value: 0n
17+
},
18+
{
19+
to: '0x2222222222222222222222222222222222222222',
20+
data: '0x5678',
21+
value: 1n
22+
}
23+
]
24+
25+
const result = SmartWallet.encodeCalls(calls)
26+
expect(result).toBeDefined()
27+
expect(result.calldata).toBeDefined()
28+
expect(result.value).toBeDefined()
29+
})
30+
31+
it('encodes batch calls with revertOnFailure option', () => {
32+
const calls: Call[] = [
33+
{
34+
to: '0x1111111111111111111111111111111111111111',
35+
data: '0x1234',
36+
value: 0n
37+
}
38+
]
39+
40+
const result = SmartWallet.encodeCalls(calls, { revertOnFailure: true })
41+
expect(result).toBeDefined()
42+
expect(result.calldata).toBeDefined()
43+
expect(result.value).toBeDefined()
44+
})
45+
46+
it('throws an error if the mode is not supported', () => {
47+
// mock getModeFromOptions
48+
jest.spyOn(SmartWallet, 'getModeFromOptions').mockReturnValue(ModeType.BATCHED_CALL_SUPPORTS_OPDATA)
49+
const calls: Call[] = [
50+
{
51+
to: '0x1111111111111111111111111111111111111111',
52+
data: '0x1234',
53+
value: 0n
54+
}
55+
]
56+
expect(() => SmartWallet.encodeCalls(calls)).toThrow()
57+
58+
jest.restoreAllMocks()
59+
})
60+
})
61+
62+
describe('createExecute', () => {
63+
it('creates an execute call for specific chain', () => {
64+
const methodParams = {
65+
calldata: EXECUTE_SELECTOR,
66+
value: 0n
67+
}
68+
69+
const call = SmartWallet.createExecute(methodParams, ChainId.MAINNET)
70+
71+
// Verify the result
72+
expect(call).toBeDefined()
73+
expect(call.to).toBe(SMART_WALLET_ADDRESSES[ChainId.MAINNET])
74+
expect(call.data).toBe(EXECUTE_SELECTOR)
75+
expect(call.value).toBe(0n)
76+
})
77+
})
78+
79+
describe('getModeFromOptions', () => {
80+
for(const canRevert of [true, false]) {
81+
it(`returns the correct mode type for canRevert: ${canRevert}`, () => {
82+
if(canRevert) {
83+
expect(SmartWallet.getModeFromOptions({ revertOnFailure: canRevert })).toBe(ModeType.BATCHED_CALL_CAN_REVERT)
84+
} else {
85+
expect(SmartWallet.getModeFromOptions({ revertOnFailure: canRevert })).toBe(ModeType.BATCHED_CALL)
86+
}
87+
})
88+
}
89+
})
90+
})

0 commit comments

Comments
 (0)