Skip to content

Commit 6135f05

Browse files
authored
Merge pull request #459 from LIT-Protocol/feat/registry-unpermit-repermit-endpoints
feat(registry): unpermit, repermit, balance API endpoints
2 parents e00ce3d + 08ba864 commit 6135f05

35 files changed

Lines changed: 3211 additions & 349 deletions
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
registry-sdk: minor
3+
contracts-sdk: minor
4+
registry-backend: minor
5+
---
6+
7+
Add uninstall endpoints, merge repermit into install-app, and add agent funds endpoint
8+
9+
**registry-backend:**
10+
11+
- Add `POST /user/:appId/uninstall-app` endpoint to initiate app uninstallation
12+
- Add `POST /user/:appId/complete-uninstall` endpoint to complete uninstall with signed data
13+
- Merge repermit logic into `POST /user/:appId/install-app` - automatically detects if user needs fresh install or re-enable
14+
- Add `POST /user/:appId/agent-funds` endpoint to fetch agent token balances via Alchemy Portfolio API
15+
- Add integration tests for uninstall and reinstall flow
16+
17+
**registry-sdk:**
18+
19+
- Add `POST /user/:appId/uninstall-app` endpoint schema
20+
- Add `POST /user/:appId/complete-uninstall` endpoint schema
21+
- Add `POST /user/:appId/agent-funds` endpoint schema
22+
- Update install-app endpoint to handle both fresh install and re-enabling previously uninstalled apps
23+
- Regenerate RTK clients
24+
25+
**contracts-sdk:**
26+
27+
- Add `deriveAgentAddress(publicClient, userControllerAddress, appId)` function for deriving agent smart account addresses
28+
- Export function from package index

docs/docs.json

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
"$schema": "https://mintlify.com/docs.json",
33
"theme": "mint",
44
"name": "Vincent Docs",
5+
"api": {
6+
"mdx": {
7+
"server": "https://api.heyvincent.ai"
8+
},
9+
"playground": {
10+
"display": "simple"
11+
}
12+
},
513
"colors": {
614
"primary": "#FF4205",
715
"light": "#FF6B35",
@@ -158,8 +166,29 @@
158166
"tab": "Wallet Providers",
159167
"groups": [
160168
{
161-
"group": "Integration",
169+
"group": "Overview",
162170
"pages": ["wallet-providers/introduction"]
171+
},
172+
{
173+
"group": "API Endpoints",
174+
"pages": [
175+
{
176+
"group": "Get App",
177+
"pages": ["wallet-providers/get-app"]
178+
},
179+
{
180+
"group": "Install App",
181+
"pages": ["wallet-providers/install-app", "wallet-providers/complete-installation"]
182+
},
183+
{
184+
"group": "Agent Details",
185+
"pages": ["wallet-providers/agent-account", "wallet-providers/agent-funds"]
186+
},
187+
{
188+
"group": "Uninstall App",
189+
"pages": ["wallet-providers/uninstall-app", "wallet-providers/complete-uninstall"]
190+
}
191+
]
163192
}
164193
]
165194
},
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: 'Get Agent Account'
3+
api: 'POST /user/{appId}/agent-account'
4+
---
5+
6+
Retrieve the user's agent smart account address for a specific app. Returns `null` if the user has not installed the app.
7+
8+
## Request
9+
10+
<ParamField path="appId" type="integer" required>
11+
The unique identifier of the app
12+
</ParamField>
13+
14+
<ParamField body="userControllerAddress" type="string" required>
15+
The Ethereum address of the user's wallet
16+
</ParamField>
17+
18+
## Response
19+
20+
<ResponseField name="agentAddress" type="string">
21+
The agent smart account address, or `null` if the app is not installed
22+
</ResponseField>
23+
24+
<RequestExample>
25+
```bash cURL
26+
curl -X POST https://api.heyvincent.ai/user/123/agent-account \
27+
-H "Content-Type: application/json" \
28+
-d '{"userControllerAddress": "0xUserWalletAddress..."}'
29+
```
30+
31+
```typescript TypeScript
32+
const response = await fetch(`https://api.heyvincent.ai/user/${appId}/agent-account`, {
33+
method: 'POST',
34+
headers: { 'Content-Type': 'application/json' },
35+
body: JSON.stringify({
36+
userControllerAddress: userWallet.address
37+
})
38+
});
39+
40+
const { agentAddress } = await response.json();
41+
42+
if (agentAddress) {
43+
console.log(`User has installed app. Agent address: ${agentAddress}`);
44+
// Display the agent address and any associated balances
45+
} else {
46+
console.log('User has not installed this app');
47+
// Show installation prompt
48+
}
49+
```
50+
</RequestExample>
51+
52+
<ResponseExample>
53+
```json Installed
54+
{
55+
"agentAddress": "0xAgentSmartAccountAddress..."
56+
}
57+
```
58+
59+
```json Not Installed
60+
{
61+
"agentAddress": null
62+
}
63+
```
64+
</ResponseExample>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
title: 'Get Agent Funds'
3+
api: 'POST /user/{appId}/agent-funds'
4+
---
5+
6+
Retrieve the token balances held by the user's agent smart account. This endpoint wraps the [Alchemy Portfolio API](https://www.alchemy.com/docs/data/portfolio-apis/portfolio-api-endpoints/portfolio-api-endpoints/get-tokens-by-address) to fetch token data across multiple networks.
7+
8+
## Request
9+
10+
<ParamField path="appId" type="integer" required>
11+
The unique identifier of the app
12+
</ParamField>
13+
14+
<ParamField body="userControllerAddress" type="string" required>
15+
The Ethereum address of the user's wallet
16+
</ParamField>
17+
18+
<ParamField body="networks" type="string[]" required>
19+
Networks to query for token balances (e.g., `["base-mainnet", "base-sepolia"]`)
20+
</ParamField>
21+
22+
## Response
23+
24+
<ResponseField name="agentAddress" type="string">
25+
The derived agent smart account address
26+
</ResponseField>
27+
28+
<ResponseField name="tokens" type="array">
29+
List of tokens held by the agent
30+
31+
<Expandable title="Token object properties">
32+
<ResponseField name="address" type="string">
33+
Wallet address that holds the token
34+
</ResponseField>
35+
<ResponseField name="network" type="string">
36+
Network identifier (e.g., `base-mainnet`)
37+
</ResponseField>
38+
<ResponseField name="tokenAddress" type="string">
39+
Token contract address
40+
</ResponseField>
41+
<ResponseField name="tokenBalance" type="string">
42+
Token balance in wei/smallest unit
43+
</ResponseField>
44+
<ResponseField name="tokenMetadata" type="object">
45+
<Expandable title="properties">
46+
<ResponseField name="decimals" type="number">
47+
Token decimals
48+
</ResponseField>
49+
<ResponseField name="logo" type="string | null">
50+
Token logo URL
51+
</ResponseField>
52+
<ResponseField name="name" type="string">
53+
Token name
54+
</ResponseField>
55+
<ResponseField name="symbol" type="string">
56+
Token symbol
57+
</ResponseField>
58+
</Expandable>
59+
</ResponseField>
60+
<ResponseField name="tokenPrices" type="array">
61+
<Expandable title="Price object properties">
62+
<ResponseField name="currency" type="string">
63+
Price currency (e.g., `usd`)
64+
</ResponseField>
65+
<ResponseField name="value" type="string">
66+
Price value
67+
</ResponseField>
68+
<ResponseField name="lastUpdatedAt" type="string">
69+
ISO timestamp of last price update
70+
</ResponseField>
71+
</Expandable>
72+
</ResponseField>
73+
<ResponseField name="error" type="string | null">
74+
Error message if applicable
75+
</ResponseField>
76+
</Expandable>
77+
</ResponseField>
78+
79+
<ResponseField name="pageKey" type="string">
80+
Pagination key for fetching more results
81+
</ResponseField>
82+
83+
<RequestExample>
84+
```bash cURL
85+
curl -X POST https://api.heyvincent.ai/user/123/agent-funds \
86+
-H "Content-Type: application/json" \
87+
-d '{
88+
"userControllerAddress": "0xUserWalletAddress...",
89+
"networks": ["base-mainnet", "base-sepolia"]
90+
}'
91+
```
92+
93+
```typescript TypeScript
94+
const response = await fetch(`https://api.heyvincent.ai/user/${appId}/agent-funds`, {
95+
method: 'POST',
96+
headers: { 'Content-Type': 'application/json' },
97+
body: JSON.stringify({
98+
userControllerAddress: userWallet.address,
99+
networks: ['base-mainnet', 'base-sepolia']
100+
})
101+
});
102+
103+
const { agentAddress, tokens } = await response.json();
104+
105+
console.log(`Agent address: ${agentAddress}`);
106+
tokens.forEach(token => {
107+
console.log(`${token.tokenMetadata?.symbol}: ${token.tokenBalance}`);
108+
});
109+
```
110+
</RequestExample>
111+
112+
<ResponseExample>
113+
```json Response
114+
{
115+
"agentAddress": "0xAgentSmartAccountAddress...",
116+
"tokens": [
117+
{
118+
"address": "0xAgentSmartAccountAddress...",
119+
"network": "base-mainnet",
120+
"tokenAddress": "0xTokenContractAddress...",
121+
"tokenBalance": "1000000000000000000",
122+
"tokenMetadata": {
123+
"decimals": 18,
124+
"logo": "https://example.com/token-logo.png",
125+
"name": "USD Coin",
126+
"symbol": "USDC"
127+
},
128+
"tokenPrices": [
129+
{
130+
"currency": "usd",
131+
"value": "1.00",
132+
"lastUpdatedAt": "2024-01-01T00:00:00Z"
133+
}
134+
]
135+
}
136+
],
137+
"pageKey": "optional-pagination-key"
138+
}
139+
```
140+
</ResponseExample>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
title: 'Complete Installation'
3+
api: 'POST /user/{appId}/complete-installation'
4+
---
5+
6+
Complete the app installation by submitting the user's signature.
7+
8+
## Request
9+
10+
<ParamField path="appId" type="integer" required>
11+
The unique identifier of the app being installed
12+
</ParamField>
13+
14+
<ParamField body="typedDataSignature" type="string" required>
15+
The user's signature of the typed data from the install-app response
16+
</ParamField>
17+
18+
<ParamField body="appInstallationDataToSign" type="object" required>
19+
The typed data object returned from the install-app endpoint
20+
</ParamField>
21+
22+
## Response
23+
24+
<ResponseField name="transactionHash" type="string">
25+
The transaction hash of the on-chain installation (gas sponsored by Vincent).
26+
</ResponseField>
27+
28+
<RequestExample>
29+
```bash cURL
30+
curl -X POST https://api.heyvincent.ai/user/123/complete-installation \
31+
-H "Content-Type: application/json" \
32+
-d '{
33+
"typedDataSignature": "0xSignature...",
34+
"appInstallationDataToSign": { "...": "..." }
35+
}'
36+
```
37+
38+
```typescript TypeScript
39+
// Step 1: Initiate installation
40+
const installResponse = await fetch(`https://api.heyvincent.ai/user/${appId}/install-app`, {
41+
method: 'POST',
42+
headers: { 'Content-Type': 'application/json' },
43+
body: JSON.stringify({
44+
userControllerAddress: userWallet.address
45+
})
46+
});
47+
const installData = await installResponse.json();
48+
49+
// Step 2: Sign the typed data with the user's wallet
50+
const { typedData } = installData.appInstallationDataToSign;
51+
const { EIP712Domain, ...types } = typedData.types;
52+
const signature = await userWallet._signTypedData(
53+
typedData.domain,
54+
types,
55+
typedData.message
56+
);
57+
58+
// Step 3: Complete the installation
59+
const response = await fetch(`https://api.heyvincent.ai/user/${appId}/complete-installation`, {
60+
method: 'POST',
61+
headers: { 'Content-Type': 'application/json' },
62+
body: JSON.stringify({
63+
typedDataSignature: signature,
64+
appInstallationDataToSign: installData.appInstallationDataToSign
65+
})
66+
});
67+
68+
const result = await response.json();
69+
console.log(`Installation tx: ${result.transactionHash}`);
70+
```
71+
</RequestExample>
72+
73+
<ResponseExample>
74+
```json Response
75+
{
76+
"transactionHash": "0xTransactionHash..."
77+
}
78+
```
79+
</ResponseExample>

0 commit comments

Comments
 (0)