Skip to content

Commit 436a83d

Browse files
committed
feat: implement coinbase mass payments across base/sol/eth/pol/arb
1 parent a9d8417 commit 436a83d

File tree

12 files changed

+1556
-445
lines changed

12 files changed

+1556
-445
lines changed

.env.example

+7-2
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,10 @@ STARKNET_ADDRESS=
9191
STARKNET_PRIVATE_KEY=
9292
STARKNET_RPC_URL=
9393

94-
# Coinbase Commerce
95-
COINBASE_COMMERCE_KEY=
94+
# Coinbase
95+
COINBASE_COMMERCE_KEY= # from coinbase developer portal
96+
COINBASE_API_KEY= # from coinbase developer portal
97+
COINBASE_PRIVATE_KEY= # from coinbase developer portal
98+
# if not configured it will be generated and written to runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_ID and runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_HEX_SEED
99+
COINBASE_GENERATED_WALLET_ID= # not your address but the wallet id from generating a wallet through the plugin
100+
COINBASE_GENERATED_WALLET_HEX_SEED= # not your address but the wallet hex seed from generating a wallet through the plugin and calling export

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,5 @@ characters/
4040
packages/core/src/providers/cache
4141
packages/core/src/providers/cache/*
4242
cache/*
43+
packages/plugin-coinbase/src/plugins/transactions.csv
44+
packages/plugin-coinbase/package-lock.json

agent/src/index.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ import {
2525
import { bootstrapPlugin } from "@ai16z/plugin-bootstrap";
2626
import { solanaPlugin } from "@ai16z/plugin-solana";
2727
import { nodePlugin } from "@ai16z/plugin-node";
28-
import { coinbaseCommercePlugin } from "@ai16z/plugin-coinbase";
28+
import {
29+
coinbaseCommercePlugin,
30+
coinbaseMassPaymentsPlugin,
31+
} from "@ai16z/plugin-coinbase";
2932
import Database from "better-sqlite3";
3033
import fs from "fs";
3134
import readline from "readline";
@@ -254,6 +257,12 @@ export function createAgent(
254257
process.env.COINBASE_COMMERCE_KEY
255258
? coinbaseCommercePlugin
256259
: null,
260+
(character.settings.secrets?.COINBASE_API_KEY ||
261+
process.env.COINBASE_API_KEY) &&
262+
(character.settings.secrets?.COINBASE_PRIVATE_KEY ||
263+
process.env.COINBASE_PRIVATE_KEY)
264+
? coinbaseMassPaymentsPlugin
265+
: null,
257266
].filter(Boolean),
258267
providers: [],
259268
actions: [],

docs/docs/packages/plugins.md

+213-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ Integrates Solana blockchain functionality:
196196
- `walletProvider` - Wallet management
197197
- `trustScoreProvider` - Transaction trust metrics
198198

199-
#### 5. Coinbase Commerce Plugin (`@eliza/plugin-coinbase-commerce`)
199+
#### 5. Coinbase Commerce Plugin (`@eliza/plugin-coinbase`)
200200

201201
Integrates Coinbase Commerce for payment and transaction management:
202202

@@ -211,6 +211,218 @@ This plugin enables Eliza to interact with the Coinbase Commerce API to create a
211211

212212
---
213213

214+
#### 6. Coinbase MassPayments Plugin (`@eliza/plugin-coinbase`)
215+
216+
This plugin facilitates the processing of cryptocurrency mass payouts using the Coinbase SDK. It enables the creation and management of mass payouts to multiple wallet addresses, logging all transaction details to a CSV file for further analysis.
217+
218+
**Actions:**
219+
220+
- `SEND_MASS_PAYOUT`
221+
Sends cryptocurrency mass payouts to multiple wallet addresses.
222+
- **Inputs**:
223+
- `receivingAddresses` (array of strings): Wallet addresses to receive funds.
224+
- `transferAmount` (number): Amount to send to each address (in smallest currency unit, e.g., Wei for ETH).
225+
- `assetId` (string): Cryptocurrency asset ID (e.g., `ETH`, `BTC`).
226+
- `network` (string): Blockchain network (e.g., `base`, `sol`, `eth`, `arb`, `pol`).
227+
- **Outputs**: Logs transaction results (success/failure) in a CSV file.
228+
- **Example**:
229+
```json
230+
{
231+
"receivingAddresses": [
232+
"0xA0ba2ACB5846A54834173fB0DD9444F756810f06",
233+
"0xF14F2c49aa90BaFA223EE074C1C33b59891826bF"
234+
],
235+
"transferAmount": 5000000000000000,
236+
"assetId": "ETH",
237+
"network": "eth"
238+
}
239+
```
240+
241+
**Providers:**
242+
243+
- `massPayoutProvider`
244+
Retrieves details of past transactions from the generated CSV file.
245+
- **Outputs**: A list of transaction records including the following fields:
246+
- `address`: Recipient wallet address.
247+
- `amount`: Amount sent.
248+
- `status`: Transaction status (`Success` or `Failed`).
249+
- `errorCode`: Error code (if any).
250+
- `transactionUrl`: URL for transaction details (if available).
251+
252+
**Description:**
253+
254+
The Coinbase MassPayments plugin streamlines cryptocurrency distribution, ensuring efficient and scalable payouts to multiple recipients on supported blockchain networks.
255+
256+
Supported networks:
257+
258+
- `base` (Base blockchain)
259+
- `sol` (Solana)
260+
- `eth` (Ethereum)
261+
- `arb` (Arbitrum)
262+
- `pol` (Polygon)
263+
264+
**Usage Instructions:**
265+
266+
1. **Configure the Plugin**
267+
Add the plugin to your character’s configuration:
268+
269+
```typescript
270+
import { coinbaseMassPaymentsPlugin } from "@eliza/plugin-coinbase-masspayments";
271+
272+
const character = {
273+
plugins: [coinbaseMassPaymentsPlugin],
274+
};
275+
```
276+
277+
2. **Ensure Secure Configuration**
278+
Set the following environment variables or runtime settings to ensure the plugin functions securely:
279+
280+
- `COINBASE_API_KEY`: API key for Coinbase SDK.
281+
- `COINBASE_PRIVATE_KEY`: Private key for secure transactions.
282+
283+
---
284+
285+
### Wallet Management
286+
287+
The plugin automatically handles wallet creation or uses an existing wallet if the required details are provided during the first run.
288+
289+
1. **Wallet Generation on First Run**
290+
If no wallet information is provided (`COINBASE_GENERATED_WALLET_HEX_SEED` and `COINBASE_GENERATED_WALLET_ID`), the plugin will:
291+
292+
- **Generate a new wallet** using the Coinbase SDK.
293+
- Automatically **export the wallet details** (`seed` and `walletId`) and securely store them in `runtime.character.settings.secrets` or other configured storage.
294+
- Log the wallet’s default address for reference.
295+
296+
2. **Using an Existing Wallet**
297+
If wallet information is available during the first run:
298+
- Provide `COINBASE_GENERATED_WALLET_HEX_SEED` and `COINBASE_GENERATED_WALLET_ID` via `runtime.character.settings.secrets` or environment variables.
299+
- The plugin will **import the wallet** and use it for processing mass payouts.
300+
301+
---
302+
303+
### Required Configurations
304+
305+
The following configurations must be provided for wallet management:
306+
307+
- **Environment Variables or Secrets**:
308+
- `COINBASE_GENERATED_WALLET_HEX_SEED`: Hexadecimal seed of the wallet.
309+
- `COINBASE_GENERATED_WALLET_ID`: Unique wallet ID.
310+
- These variables must be securely stored in `runtime.character.settings.secrets` or as environment variables.
311+
312+
---
313+
314+
### Wallet Creation Process
315+
316+
1. **Automatic Wallet Creation**
317+
When no wallet details are available:
318+
319+
- A new wallet is created using the Coinbase SDK.
320+
- The wallet’s `seed` and `walletId` are retrieved using the following logic:
321+
```typescript
322+
const walletData: WalletData = wallet.export();
323+
runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_HEX_SEED =
324+
walletData.seed;
325+
runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_ID =
326+
walletData.walletId;
327+
```
328+
- The default wallet address is logged:
329+
```typescript
330+
const walletAddress = wallet.getDefaultAddress();
331+
elizaLogger.log("Created and stored new wallet:", walletAddress);
332+
```
333+
334+
2. **Using Existing Wallet Details**
335+
When the wallet details are provided:
336+
- The wallet is imported using the following logic:
337+
```typescript
338+
wallet = await Wallet.import({
339+
seed: storedSeed,
340+
walletId: storedWalletId,
341+
});
342+
elizaLogger.log("Imported existing wallet:", wallet.getDefaultAddress());
343+
```
344+
345+
---
346+
347+
### Example Configuration
348+
349+
#### Automatic Wallet Generation:
350+
351+
No existing wallet information is passed. The plugin creates and stores a new wallet:
352+
353+
```typescript
354+
runtime.character.settings.secrets = {
355+
// Empty settings for first run
356+
};
357+
```
358+
359+
Output Log:
360+
361+
```plaintext
362+
[INFO] Created and stored new wallet: 0x1234567890abcdef1234567890abcdef12345678
363+
```
364+
365+
#### Using Existing Wallet Information:
366+
367+
Existing wallet details are passed into the runtime:
368+
369+
```typescript
370+
runtime.character.settings.secrets = {
371+
COINBASE_GENERATED_WALLET_HEX_SEED:
372+
"0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
373+
COINBASE_GENERATED_WALLET_ID: "wallet-id-123",
374+
};
375+
```
376+
377+
Output Log:
378+
379+
```plaintext
380+
[INFO] Imported existing wallet: 0x1234567890abcdef1234567890abcdef12345678
381+
```
382+
383+
---
384+
385+
3. **Example Call**
386+
An example of using the `SEND_MASS_PAYOUT` action:
387+
388+
```typescript
389+
const response = await runtime.triggerAction("SEND_MASS_PAYOUT", {
390+
receivingAddresses: [
391+
"0xA0ba2ACB5846A54834173fB0DD9444F756810f06",
392+
"0xF14F2c49aa90BaFA223EE074C1C33b59891826bF",
393+
],
394+
transferAmount: 5000000000000000, // 0.005 ETH
395+
assetId: "ETH",
396+
network: "eth",
397+
});
398+
console.log("Mass payout response:", response);
399+
```
400+
401+
4. **Transaction Logging**
402+
All transactions (successful and failed) are logged to a `transactions.csv` file in the plugins working directory:
403+
```plaintext
404+
Address,Amount,Status,Error Code,Transaction URL
405+
0xA0ba2ACB5846A54834173fB0DD9444F756810f06,5000000000000000,Success,,https://etherscan.io/tx/0x...
406+
```
407+
408+
**Example Output:**
409+
410+
When successful, a response similar to the following will be returned:
411+
412+
```json
413+
{
414+
"text": "Mass payouts completed successfully.\n- Successful Transactions: 2\n- Failed Transactions: 0\nCheck the CSV file for more details."
415+
}
416+
```
417+
418+
**Best Practices:**
419+
420+
- **Secure Secrets Storage**: Ensure `COINBASE_API_KEY` and `COINBASE_PRIVATE_KEY` are stored securely in `runtime.character.settings.secrets` or environment variables. Either add `COINBASE_GENERATED_WALLET_HEX_SEED`, and `COINBASE_GENERATED_WALLET_ID` from a previous run, or it will be dynamically created
421+
- **Validation**: Always validate input parameters, especially `receivingAddresses` and `network`, to ensure compliance with expected formats and supported networks.
422+
- **Error Handling**: Monitor logs for failed transactions or errors in the payout process and adjust retry logic as needed.
423+
424+
---
425+
214426
### Writing Custom Plugins
215427

216428
Create a new plugin by implementing the Plugin interface:

package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"prepare": "husky install"
2525
},
2626
"devDependencies": {
27+
"@commitlint/cli": "^18.4.4",
28+
"@commitlint/config-conventional": "^18.4.4",
2729
"concurrently": "^9.1.0",
2830
"husky": "^9.1.6",
2931
"lerna": "^8.1.5",
@@ -32,9 +34,7 @@
3234
"typedoc": "^0.26.11",
3335
"typescript": "5.6.3",
3436
"vite": "^5.4.11",
35-
"vitest": "^2.1.5",
36-
"@commitlint/cli": "^18.4.4",
37-
"@commitlint/config-conventional": "^18.4.4"
37+
"vitest": "^2.1.5"
3838
},
3939
"pnpm": {
4040
"overrides": {
@@ -45,6 +45,8 @@
4545
"node": ">=22"
4646
},
4747
"dependencies": {
48+
"@coinbase/coinbase-sdk": "^0.10.0",
49+
"csv-parse": "^5.6.0",
4850
"ollama-ai-provider": "^0.16.1",
4951
"optional": "^0.1.4",
5052
"sharp": "^0.33.5",

0 commit comments

Comments
 (0)