Skip to content

Commit

Permalink
Feat/batch upload (#174)
Browse files Browse the repository at this point in the history
* feat: Feegrant

* feat: Feegrant

* feat: Feegrant grantAllowance API

* feat: CreateObject example

* feat: CreateBucket example

* feat: Transfer example

* feat: AuthInfoBytes add feePayer and feeGranter
  • Loading branch information
rrr523 authored Jul 20, 2023
1 parent e2ebac6 commit 4d88665
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 116 deletions.
5 changes: 5 additions & 0 deletions .changeset/selfish-pianos-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@bnb-chain/greenfield-chain-sdk': patch
---

feat: AuthInfoBytes add feePayer and feeGranter
143 changes: 103 additions & 40 deletions examples/nextjs/src/components/batch/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { client, selectSp } from '@/client';
import { GRNToString, newBucketGRN, PermissionTypes } from '@bnb-chain/greenfield-chain-sdk';
import {
GRNToString,
MsgCreateObjectTypeUrl,
newBucketGRN,
PermissionTypes,
} from '@bnb-chain/greenfield-chain-sdk';
import { Wallet } from '@ethersproject/wallet';
import { useState } from 'react';
import { useAccount, useNetwork } from 'wagmi';
import { parseEther } from 'viem';
import { useAccount } from 'wagmi';

export const FeeGrant = () => {
const { address } = useAccount();
const [bucketName, setBucketName] = useState<string>('');
const [objectName, setObjectName] = useState<string>('');
const [wallet, setWallet] = useState<Wallet | null>(null);

return (
<>
Expand All @@ -19,68 +27,123 @@ export const FeeGrant = () => {
setBucketName(e.target.value);
}}
/>
object name :
<input
value={objectName}
placeholder="object name"
onChange={(e) => {
setObjectName(e.target.value);
}}
/>
<br />
<button
onClick={async () => {
if (!address) return;

// select sp to be primary sp
const spInfo = await selectSp();
const { primarySpAddress } = spInfo;

/* await client.bucket.createBucket({
bucketName,
creator: address,
visibility: 'VISIBILITY_TYPE_PUBLIC_READ',
chargedReadQuota: '0',
spInfo,
signType: 'authTypeV2',
}); */

const { sequence } = await client.account.getAccount(address);
console.log('sequence', sequence);

// 1. create temporary account
const wallet = Wallet.createRandom();
console.log('wallet', wallet, wallet.privateKey);
console.log('wallet', wallet.address, wallet.privateKey);
setWallet(wallet);

// 2. allow temporary account to submit specified tx and amount
const grantAllowanceTx = await client.feegrant.grantAllowance({
granter: address,
grantee: wallet.address,
// allowance: ,
allowedMessages: [MsgCreateObjectTypeUrl],
amount: parseEther('0.1').toString(),
denom: 'BNB',
});

const simulateInfo = await grantAllowanceTx.simulate({
// 3. Put bucket policy so that the temporary account can create objects within this bucket
const statement: PermissionTypes.Statement = {
effect: PermissionTypes.Effect.EFFECT_ALLOW,
actions: [PermissionTypes.ActionType.ACTION_CREATE_OBJECT],
resources: [GRNToString(newBucketGRN(bucketName))],
};
const putPolicyTx = await client.bucket.putBucketPolicy(bucketName, {
operator: address,
statements: [statement],
principal: {
type: PermissionTypes.PrincipalType.PRINCIPAL_TYPE_GNFD_ACCOUNT,
value: wallet.address,
},
});

// 4. broadcast txs include 2 msg
const txs = await client.basic.multiTx([grantAllowanceTx, putPolicyTx]);
const simuluateInfo = await txs.simulate({
denom: 'BNB',
});

console.log('simuluateInfo', simuluateInfo);
const res = await txs.broadcast({
denom: 'BNB',
gasLimit: Number(210000),
gasPrice: '5000000000',
payer: address,
granter: '',
});

console.log('simulateInfo:', simulateInfo);
console.log('res', res);

const res = await grantAllowanceTx.broadcast({
if (res.code === 0) {
alert('success');
}
}}
>
feegrant
</button>
<br />
<button
onClick={async () => {
if (!address || !wallet) return;

const granteeAddr = wallet.address;
const privateKey = wallet.privateKey;

const spInfo = await selectSp();
const createFolderTx = await client.object.createFolder({
bucketName,
objectName: objectName + '/',
spInfo,
creator: granteeAddr,
signType: 'authTypeV2',
});

const simulateInfo = await createFolderTx.simulate({
denom: 'BNB',
});
console.log('simulateInfo', simulateInfo);
const txres = await createFolderTx.broadcast({
denom: 'BNB',
gasLimit: Number(simulateInfo?.gasLimit),
gasPrice: simulateInfo?.gasPrice || '5000000000',
payer: address,
granter: '',
payer: granteeAddr,
granter: address,
privateKey,
});

console.log(res);
console.log('txres', txres);

// const statement: PermissionTypes.Statement = {
// effect: PermissionTypes.Effect.EFFECT_ALLOW,
// actions: [PermissionTypes.ActionType.ACTION_UPDATE_BUCKET_INFO],
// resources: [GRNToString(newBucketGRN(bucketName))],
// };
// const putPolicyTx = await client.bucket.putBucketPolicy(bucketName, {
// operator: address,
// statements: [statement],
// principal: {
// type: PermissionTypes.PrincipalType.PRINCIPAL_TYPE_GNFD_ACCOUNT,
// value: '0x0000000000000000000000000000000000000001',
// },
// });
if (txres.code === 0) {
alert('success');
}
}}
>
create object
</button>
<br />
<button
onClick={async () => {
if (!address || !wallet) return;

const res = await client.feegrant.getAllowences({
grantee: wallet.address,
});
console.log('res', res);
}}
>
go batch
get fee grant
</button>
</>
);
Expand Down
22 changes: 17 additions & 5 deletions packages/chain-sdk/src/api/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ export class Basic implements IBasic {
opts,
);

// console.log('txRaw', bufferToHex(Buffer.from(rawTxBytes)));

return await this.broadcastRawTx(rawTxBytes);
},
metaTxInfo: {
Expand Down Expand Up @@ -211,6 +213,8 @@ export class Basic implements IBasic {
gasLimit: 0,
gasPrice: '0',
pubKey: makeCosmsPubKey(ZERO_PUBKEY),
granter: '',
payer: '',
});
const tx = Tx.fromPartial({
authInfo: AuthInfo.decode(authInfoBytes),
Expand Down Expand Up @@ -255,6 +259,7 @@ export class Basic implements IBasic {
gasPrice,
privateKey,
payer,
granter,
signTypedDataCallback = defaultSignTypedData,
} = opts;

Expand All @@ -264,7 +269,7 @@ export class Basic implements IBasic {
denom,
String(gasLimit),
payer,
'',
granter,
);
const wrapperTypes = generateTypes(types);
const multiMessages = mergeMultiMessage(txs);
Expand Down Expand Up @@ -294,6 +299,8 @@ export class Basic implements IBasic {
gasLimit,
gasPrice,
pubKey,
granter,
payer,
});

const txRaw = TxRaw.fromPartial({
Expand All @@ -308,12 +315,12 @@ export class Basic implements IBasic {
}

private getAuthInfoBytes(
params: Pick<BroadcastOptions, 'denom' | 'gasLimit' | 'gasPrice'> & {
params: Pick<BroadcastOptions, 'denom' | 'gasLimit' | 'gasPrice' | 'granter' | 'payer'> & {
pubKey: BaseAccount['pubKey'];
sequence: string;
},
) {
const { pubKey, denom = DEFAULT_DENOM, sequence, gasLimit, gasPrice } = params;
const { pubKey, denom = DEFAULT_DENOM, sequence, gasLimit, gasPrice, granter, payer } = params;
if (!pubKey) throw new Error('pubKey is required');

const feeAmount: Coin[] = [
Expand All @@ -322,8 +329,8 @@ export class Basic implements IBasic {
amount: String(BigInt(gasLimit) * BigInt(gasPrice)),
},
];
const feeGranter = undefined;
const feePayer = undefined;
const feeGranter = granter;
const feePayer = payer;
const authInfoBytes = makeAuthInfoBytes(
[{ pubkey: pubKey, sequence: Number(sequence) }],
feeAmount,
Expand Down Expand Up @@ -355,6 +362,8 @@ export class Basic implements IBasic {
gasPrice,
privateKey,
signTypedDataCallback = defaultSignTypedData,
granter,
payer,
} = txOption;
const eip712 = this.getEIP712Struct(
typeUrl,
Expand Down Expand Up @@ -383,6 +392,7 @@ export class Basic implements IBasic {
msgEIP712,
txOption,
);
// console.log('signature', signature);
} else {
signature = await signTypedDataCallback(accountInfo.address, JSON.stringify(eip712));
const messageHash = eip712Hash(JSON.stringify(eip712));
Expand All @@ -405,6 +415,8 @@ export class Basic implements IBasic {
gasLimit,
gasPrice,
pubKey,
granter,
payer,
});

const txRaw = TxRaw.fromPartial({
Expand Down
73 changes: 19 additions & 54 deletions packages/chain-sdk/src/api/feegrant.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { MsgGrantAllowanceSDKTypeEIP712 } from '@/messages/feegrant/MsgGrantAllowance';
import { MsgRevokeAllowanceSDKTypeEIP712 } from '@/messages/feegrant/MsgRevokeAllowance';
import {
AllowedMsgAllowance,
BasicAllowance,
} from '@bnb-chain/greenfield-cosmos-types/cosmos/feegrant/v1beta1/feegrant';
import {
QueryAllowanceRequest,
QueryAllowanceResponse,
Expand All @@ -14,21 +10,24 @@ import {
MsgGrantAllowance,
MsgRevokeAllowance,
} from '@bnb-chain/greenfield-cosmos-types/cosmos/feegrant/v1beta1/tx';
import { Any } from '@bnb-chain/greenfield-cosmos-types/google/protobuf/any';
import { Timestamp } from '@bnb-chain/greenfield-cosmos-types/google/protobuf/timestamp';
import {
base64FromBytes,
bytesFromBase64,
toTimestamp,
} from '@bnb-chain/greenfield-cosmos-types/helpers';
import { toBuffer } from '@ethereumjs/util';
import { container, singleton } from 'tsyringe';
import { encodeToHex, MsgGrantAllowanceTypeUrl, MsgRevokeAllowanceTypeUrl, TxResponse } from '..';
import {
encodeToHex,
IGrantAllowance,
MsgGrantAllowanceTypeUrl,
MsgRevokeAllowanceTypeUrl,
newAllowedMsgAllowance,
newBasicAllowance,
newMarshal,
newMsgGrantAllowance,
TxResponse,
} from '..';
import { Basic } from './basic';
import { RpcQueryClient } from './queryclient';

export interface IFeeGrant {
grantAllowance(msg: MsgGrantAllowance): Promise<TxResponse>;
grantAllowance(msg: IGrantAllowance): Promise<TxResponse>;

revokeAllowance(msg: MsgRevokeAllowance): Promise<TxResponse>;

Expand All @@ -42,51 +41,17 @@ export class FeeGrant implements IFeeGrant {
private basic: Basic = container.resolve(Basic);
private queryClient: RpcQueryClient = container.resolve(RpcQueryClient);

public async grantAllowance(msg: MsgGrantAllowance) {
const basicAllowance: BasicAllowance = {
spendLimit: [
{
amount: '111',
denom: 'BNB',
},
],
};
public async grantAllowance(params: IGrantAllowance) {
const { amount, denom, allowedMessages, grantee, granter } = params;

const allowedMsgAllowance: AllowedMsgAllowance = {
allowedMessages: ['/greenfield.storage.MsgCreateObject'],
allowance: Any.fromPartial({
typeUrl: '/cosmos.feegrant.v1beta1.BasicAllowance',
value: BasicAllowance.encode(basicAllowance).finish(),
}),
};

const grantAllowance: MsgGrantAllowance = {
...msg,
allowance: Any.fromPartial({
typeUrl: '/cosmos.feegrant.v1beta1.AllowedMsgAllowance',
value: AllowedMsgAllowance.encode(allowedMsgAllowance).finish(),
}),
};

const marshal = {
'@type': '/cosmos.feegrant.v1beta1.AllowedMsgAllowance',
allowed_messages: ['/greenfield.storage.MsgCreateObject'],
allowance: Any.fromPartial({
typeUrl: '/cosmos.feegrant.v1beta1.BasicAllowance',
value: BasicAllowance.encode(basicAllowance).finish(),
}),
// expiration: null,
// spend_limit: [
// {
// amount: '111',
// denom: 'BNB',
// },
// ],
};
const basicAllowance = newBasicAllowance(amount, denom);
const allowedMsgAllowance = newAllowedMsgAllowance(allowedMessages, basicAllowance);
const grantAllowance = newMsgGrantAllowance(grantee, granter, allowedMsgAllowance);
const marshal = newMarshal(amount, denom, allowedMessages);

return await this.basic.tx(
MsgGrantAllowanceTypeUrl,
msg.granter,
granter,
MsgGrantAllowanceSDKTypeEIP712,
{
...MsgGrantAllowance.toSDK(grantAllowance),
Expand Down
2 changes: 2 additions & 0 deletions packages/chain-sdk/src/constants/typeUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ export const MsgUpdateBucketInfoTypeUrl = '/greenfield.storage.MsgUpdateBucketIn
export const MsgUpdateGroupExtraTypeUrl = '/greenfield.storage.MsgUpdateGroupExtra';
export const MsgUpdateGroupMemberTypeUrl = '/greenfield.storage.MsgUpdateGroupMember';
export const MsgUpdateObjectInfoTypeUrl = '/greenfield.storage.MsgUpdateObjectInfo';
export const BasicAllowanceTypeUrl = '/cosmos.feegrant.v1beta1.BasicAllowance';
export const AllowedMsgAllowanceTypeUrl = '/cosmos.feegrant.v1beta1.AllowedMsgAllowance';
Loading

0 comments on commit 4d88665

Please sign in to comment.