Skip to content

Commit

Permalink
wip state management
Browse files Browse the repository at this point in the history
  • Loading branch information
deansallinen committed Jun 14, 2024
1 parent 5cddd08 commit fd502fc
Show file tree
Hide file tree
Showing 15 changed files with 332 additions and 181 deletions.
File renamed without changes.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"eslint-plugin-promise": "^6.1.1",
"prettier": "^2.7.1",
"prettier-plugin-packagejson": "^2.2.18",
"prettier-plugin-svelte": "^3.2.4",
"sharp": "^0.32.6",
"typescript": "^4.7.4",
"typescript-eslint": "^8.0.0-alpha.20"
Expand Down
73 changes: 73 additions & 0 deletions packages/site/src/components/CreateAccount.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script lang="ts">
import { connectAccount } from '$lib/rpc-methods';
let account = { name: '', publicKey: '' };
type AccountData = {
accountName: string;
activeKey: string;
ownerKey: string;
chainId: string;
};
async function handleFormSubmit(event: Event) {
const formData = new FormData(event.target as HTMLFormElement);
const accountData: AccountData = {
accountName: formData.get('account') as string,
activeKey: formData.get('publicKey') as string,
ownerKey: formData.get('publicKey') as string,
chainId: '73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d'
};
const name = await createAccount(accountData, 'https://jungle4.greymass.com');
if (typeof name !== 'undefined') {
console.log(`Account ${name} created`);
connectAccount();
}
}
async function createAccount(accountData: AccountData, chainUrl: string) {
const data = {
accountName: accountData.accountName,
activeKey: accountData.activeKey,
ownerKey: accountData.ownerKey,
network: accountData.chainId
};
try {
const response = await fetch(`${chainUrl}/account/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
console.log(response);
if (response.status === 201) {
console.log('success:', JSON.stringify(await response.text(), null, 2));
return accountData.accountName;
}
console.log('failure:', JSON.stringify(await response.text(), null, 2));
} catch (error) {
console.error('error getting response', error);
}
}
</script>

<h2>Create Jungle4 Account</h2>
<form on:submit|preventDefault={handleFormSubmit}>
<div>
<label for="account">Name:</label>
<input type="text" id="account" name="account" bind:value={account.name} />
</div>
<div>
<label for="account">PublicKey:</label>
<input type="text" id="account" name="publicKey" bind:value={account.publicKey} />
</div>
<button type="submit">Submit</button>
</form>
4 changes: 4 additions & 0 deletions packages/site/src/lib/account.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { writable } from 'svelte/store';

export const accountName = writable<string | null>(null);

export const accountPermission = writable<string | null>(null);

export const accountPublicKey = writable<string | null>(null);
21 changes: 21 additions & 0 deletions packages/site/src/lib/rpc-methods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { accountName } from './account';
import { invokeSnap } from './snap';

export async function connectAccount() {
const result = await invokeSnap({ method: 'eos_connectAccount' });
console.log('accountName', result);
accountName.set(result);
}

export async function getConnectedAccount() {
const account = await invokeSnap({ method: 'eos_getConnectedAccount' });
console.log('account', account);
accountName.set(account);
// accountPublicKey.set(account.publicKey);
// accountPermission.set(account.permission);
}

export async function testTransaction() {
const result = await invokeSnap({ method: 'eos_signTransaction' });
console.log('result', result);
}
90 changes: 9 additions & 81 deletions packages/site/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import { setSnap, requestSnap, invokeSnap } from '$lib/snap';
import flask_fox from '../assets/flask_fox.svg';
import { accountName } from '$lib/account';
import CreateAccount from '../components/CreateAccount.svelte';
import { connectAccount, getConnectedAccount, testTransaction } from '$lib/rpc-methods';
// import type {RpcMethodTypes} from '@greymass/eos-snap';
let provider: MetaMaskInpageProvider;
Expand All @@ -19,76 +21,9 @@
provider = $snapProvider; // gotta be a better way of narrowing this type
isFlask.set(await checkIsFlask(provider));
setSnap();
getConnectedAccount();
}
});
async function connectAccount() {
const result = await invokeSnap({ method: 'eos_connectAccount' });
console.log('accountName', result);
accountName.set(result);
}
let account = { name: '', publicKey: '' };
type AccountData = {
accountName: string;
activeKey: string;
ownerKey: string;
chainId: string;
};
async function handleFormSubmit(event: Event) {
const formData = new FormData(event.target as HTMLFormElement);
const accountData: AccountData = {
accountName: formData.get('account') as string,
activeKey: formData.get('publicKey') as string,
ownerKey: formData.get('publicKey') as string,
chainId: '73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d'
};
const name = await createAccount(accountData, 'https://jungle4.greymass.com');
if (typeof name !== 'undefined') {
console.log(`Account ${name} created`);
connectAccount();
}
}
async function createAccount(accountData: AccountData, chainUrl: string) {
const data = {
accountName: accountData.accountName,
activeKey: accountData.activeKey,
ownerKey: accountData.ownerKey,
network: accountData.chainId
};
try {
const response = await fetch(`${chainUrl}/account/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
console.log(response);
if (response.status === 201) {
console.log('success:', JSON.stringify(await response.text(), null, 2));
return accountData.accountName;
}
console.log('failure:', JSON.stringify(await response.text(), null, 2));
} catch (error) {
console.error('error getting response', error);
}
}
async function testTransaction() {
const result = await invokeSnap({ method: 'eos_signTransaction' });
console.log('result', result);
}
</script>

<h1>
Expand All @@ -101,27 +36,20 @@
<p>Is flask: {$isFlask}</p>
<p>Is metamask ready: {$isMetaMaskReady}</p>
<p>Is snap installed: {isSnapInstalled}</p>
<!-- <p>Installed snap: {JSON.stringify($installedSnap)}</p> -->

<button on:click={() => requestSnap()} disabled={!$isMetaMaskReady}>
<img src={flask_fox} alt="Flask Fox" width="20" height="20" />
Install Snap
{isSnapInstalled ? 'Re-install snap' : 'Install snap'}
</button>

<p>The snap will need to be re-installed after any changes to the code.</p>

<button on:click={connectAccount} disabled={!$isMetaMaskReady}> Connect EOS Account </button>

<p>We disable the connection button when an account is already connected.</p>

<button on:click={testTransaction} disabled={!$isMetaMaskReady}>Test Signing Transaction</button>
<hr />

<h2>Create Jungle4 Account</h2>
<form on:submit|preventDefault={handleFormSubmit}>
<div>
<label for="account">Name:</label>
<input type="text" id="account" name="account" bind:value={account.name} />
</div>
<div>
<label for="account">PublicKey:</label>
<input type="text" id="account" name="publicKey" bind:value={account.publicKey} />
</div>
<button type="submit">Submit</button>
</form>
<CreateAccount />
3 changes: 2 additions & 1 deletion packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/template-snap-monorepo.git"
},
"source": {
"shasum": "T9QRYWyyVoAGWQZTG3ByknNkPuBK8CI6OQxkQ96gg5I=",
"shasum": "jyxS5srKby/XVNf3aoW+WrKPwt1e6NPo9aRhJyaY+E8=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand All @@ -17,6 +17,7 @@
}
},
"initialPermissions": {
"snap_manageState": {},
"snap_dialog": {},
"endowment:page-home": {},
"endowment:network-access": {},
Expand Down
49 changes: 27 additions & 22 deletions packages/snap/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { APIClient, Name, PermissionLevel, PublicKey, SignedTransaction, TransactionHeader, UInt32, Weight } from '@wharfkit/antelope';
import {
APIClient as AntelopeAPIClient,
Name,
PermissionLevel,
PublicKey,
TransactionHeader,
UInt32,
Weight,
} from '@wharfkit/antelope';
import { Account } from '../models';

type NetworkAccount = {
Expand All @@ -8,51 +16,48 @@ type NetworkAccount = {
authorizing_account: PermissionLevel;
weight: Weight;
threshold: UInt32;
}
};

interface APIGateway {
fetchAccounts(publicKey: PublicKey): Promise<Account[]>;
fetchAccountByKey(publicKey: PublicKey): Promise<Account | null>;
getTransactionHeader(secondsAhead?: number): Promise<TransactionHeader>;
pushTransaction(signedTransaction: SignedTransaction): Promise<string>;
}

export class Client implements APIGateway {
client: APIClient
chainUrl: string
export class ApiClient implements APIGateway {
client: AntelopeAPIClient;

constructor(chainUrl: string) {
this.client = new APIClient({url: chainUrl});
this.chainUrl = chainUrl;
constructor(url: string) {
this.client = new AntelopeAPIClient({ url });
}

private dataToAccount(data: NetworkAccount): Account {
private accountDecoder(data: NetworkAccount): Account {
return new Account(
String(data.account_name),
String(data.permission_name),
String(data.authorizing_key)
String(data.authorizing_key),
);
}

public async fetchAccounts(publicKey: PublicKey) {
const { accounts } = await this.client.v1.chain.get_accounts_by_authorizers({keys: [publicKey.toString()]});
return accounts.map(this.dataToAccount);
const { accounts } = await this.client.v1.chain.get_accounts_by_authorizers(
{ keys: [publicKey.toString()] },
);
return accounts.map(this.accountDecoder);
}

public async fetchAccountByKey(publicKey: PublicKey) {
const { accounts } = await this.client.v1.chain.get_accounts_by_authorizers({keys: [publicKey.toString()]});
const { accounts } = await this.client.v1.chain.get_accounts_by_authorizers(
{ keys: [publicKey.toString()] },
);
if (!accounts[0]) return null;
const [account] = accounts;
return this.dataToAccount(account);
return this.accountDecoder(account);
}

public async getTransactionHeader(secondsAhead = 90) {
return this.client.v1.chain.get_info().then(info => info.getTransactionHeader(secondsAhead));
return this.client.v1.chain
.get_info()
.then((info) => info.getTransactionHeader(secondsAhead));
}

public async pushTransaction(signedTransaction: SignedTransaction) {
const result = await this.client.v1.chain.push_transaction(signedTransaction);
return String(result.processed);
}

}
19 changes: 12 additions & 7 deletions packages/snap/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { OnRpcRequestHandler } from '@metamask/snaps-sdk';
import { signTransaction, connectAccount } from './rpc';
import {
type OnRpcRequestHandler,
MethodNotFoundError,
} from '@metamask/snaps-sdk';

import { signTransaction, connectAccount, getConnectedAccount } from './rpc';

export * from './rpc-types';

Expand All @@ -13,18 +17,19 @@ export * from './rpc-types';
* @returns The result of `snap_dialog`.
* @throws If the request method is not valid for this snap.
*/
export const onRpcRequest: OnRpcRequestHandler = async ({
request,
}) => {
export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
switch (request.method) {
case 'eos_connectAccount':
return await connectAccount();

case 'eos_signTransaction':
return await signTransaction();

case 'eos_getConnectedAccount':
return await getConnectedAccount();

default:
throw new Error('Method not found.');
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw new MethodNotFoundError(request.method);
}
};

Loading

0 comments on commit fd502fc

Please sign in to comment.