Ethereum Kit
Storage Account
The ShelbyStorageAccount class for Ethereum wallets
The ShelbyStorageAccount class represents a Shelby storage account derived from an Ethereum wallet. It extends EIP1193DerivedAccount from @aptos-labs/derived-wallet-ethereum.
Overview
A storage account:
- Is derived deterministically from an Ethereum address and domain
- Can sign transactions on behalf of the Ethereum wallet
- Owns blobs stored on Shelby
Import
import { ShelbyStorageAccount } from "@shelby-protocol/ethereum-kit/node";Creation
The recommended way to create a storage account is via the Shelby client:
import { Shelby, Network } from "@shelby-protocol/ethereum-kit/node";
import { Wallet } from "ethers";
const shelbyClient = new Shelby({
network: Network.SHELBYNET,
apiKey: "AG-***",
});
const ethereumWallet = new Wallet("0x...private_key...");
const storageAccount = shelbyClient.createStorageAccount(
ethereumWallet,
"my-dapp.com"
);Or directly:
import { ShelbyStorageAccount } from "@shelby-protocol/ethereum-kit/node";
import { Wallet } from "ethers";
const ethereumWallet = new Wallet("0x...private_key...");
const storageAccount = new ShelbyStorageAccount({
ethereumWallet,
domain: "my-dapp.com",
});Properties
| Property | Type | Description |
|---|---|---|
accountAddress | AccountAddress | The derived Aptos account address |
ethereumWallet | Wallet | The underlying ethers.js Wallet |
domain | string | The domain used for derivation |
scheme | string | The URI scheme (default: "https") |
derivedPublicKey | EIP1193DerivedPublicKey | The derived public key instance |
authenticationFunction | string | The authentication function identifier |
Address Derivation
The storage account address is deterministically derived from:
- The Ethereum wallet's address
- The domain string
// Same wallet + domain = same storage account address
const account1 = shelbyClient.createStorageAccount(wallet, "app.com");
const account2 = shelbyClient.createStorageAccount(wallet, "app.com");
account1.accountAddress.toString() === account2.accountAddress.toString(); // true
// Different domain = different storage account address
const account3 = shelbyClient.createStorageAccount(wallet, "other.com");
account1.accountAddress.toString() !== account3.accountAddress.toString(); // trueDomain scoping provides application-level isolation. The same Ethereum wallet will have different storage accounts on different dApps.
Using as a Signer
The storage account implements the signer interface required by Shelby operations:
// Upload with the storage account as signer
await shelbyClient.upload({
blobData: new Uint8Array([1, 2, 3]),
signer: storageAccount,
blobName: "example.txt",
expirationMicros: Date.now() * 1000 + 86400000000,
});Methods
The ShelbyStorageAccount inherits methods from AbstractedAccount:
| Method | Description |
|---|---|
sign(message) | Signs a message with the Ethereum wallet |
signTransactionWithAuthenticator(transaction) | Signs and wraps for Aptos submission |
Complete Example
import { Shelby, Network } from "@shelby-protocol/ethereum-kit/node";
import { Wallet } from "ethers";
async function main() {
const shelbyClient = new Shelby({
network: Network.SHELBYNET,
apiKey: "AG-***",
});
// Create wallet from private key
const ethereumWallet = new Wallet(process.env.ETHEREUM_PRIVATE_KEY!);
console.log("Ethereum Address:", ethereumWallet.address);
// Create storage account
const storageAccount = shelbyClient.createStorageAccount(
ethereumWallet,
"my-dapp.com"
);
console.log("Storage Account:", storageAccount.accountAddress.toString());
console.log("Domain:", storageAccount.domain);
console.log("Scheme:", storageAccount.scheme);
// Fund and upload
await shelbyClient.fundAccountWithShelbyUSD({
address: storageAccount.accountAddress,
amount: 1_000_000,
});
await shelbyClient.fundAccountWithAPT({
address: storageAccount.accountAddress,
amount: 1_000_000,
});
await shelbyClient.upload({
blobData: new TextEncoder().encode("Hello from Ethereum!"),
signer: storageAccount,
blobName: "hello.txt",
expirationMicros: Date.now() * 1000 + 86400000000,
});
console.log("Upload complete!");
}
main().catch(console.error);