Skip to content

Commit

Permalink
Add more grant types which can be requested from a dapp (#107)
Browse files Browse the repository at this point in the history
* Add support for bank grants

This update enhances the Abstraxion dashboard by adding support for bank grants. It now handles "bank" as a query parameter, thus allowing for the specification of bandwidth spending limits. The commit also entails modifying the AbstraxionProvider and AbstraxionSignin components to accommodate this feature. Additional helper functions for generating stake, bank and contract grants were introduced.

* Add staking capability to Abstraxion components

Added a "stake" boolean configuration option to the Abstraxion, AbstraxionContext and AbstraxionSignin components. When set to true, stake-related authorization grants are generated in addition to contract grants in the AbstraxionGrant component. This allows users to stake assets using the Abstraxion interface.
  • Loading branch information
justinbarry authored Feb 26, 2024
1 parent 6de3996 commit 2c33c31
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 86 deletions.
40 changes: 40 additions & 0 deletions .changeset/spotty-cats-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
"abstraxion-dashboard": minor
"@burnt-labs/abstraxion": minor
"demo-app": minor
---

Staking Grants
===
Add the ability for dapps to request staking grants be give via the dashboard. To request a grant, the dapp will need to set the `stake` prop to `true` in the config of the `abstraxion` provider.

```jsx
<AbstraxionProvider
config={{
stake: true,
}}
>
{children}
</AbstraxionProvider>
```

This will grant `StakeAuthorization` to delegate, undelegate, redelegate and a GenericAuthorization to exec a MsgWithdrawDelegatorReward msg along with a feegrant for these message to cover the fees.

Bank Send Grants
===
Add the ability for dapps to request bank send grants be give via the dashboard. To request a grant, the dapp will need to set pass the requested `denom` and `amount` to the config of the `abstraxion` provider.

```jsx
<AbstraxionProvider
config={{
bank: [
{
denom: "uxion",
amount: "1000000",
},
],
}}
>
{children}
</AbstraxionProvider>
```
4 changes: 3 additions & 1 deletion apps/abstraxion-dashboard/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ export default function Home() {
const accountBalance = useAccountBalance(account, client);

const contracts = searchParams.get("contracts");
const stake = Boolean(searchParams.get("stake"));
const bank = searchParams.get("bank");
const grantee = searchParams.get("grantee");

return (
<>
{!account?.id || (contracts && grantee) ? (
{!account?.id || (grantee && (contracts || stake || bank)) ? (
<div className="ui-flex ui-h-screen ui-flex-1 ui-items-center ui-justify-center ui-overflow-y-auto ui-p-6">
<Abstraxion onClose={() => null} isOpen={true} />
</div>
Expand Down
23 changes: 21 additions & 2 deletions apps/abstraxion-dashboard/components/Abstraxion/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ export const Abstraxion = ({ isOpen, onClose }: ModalProps) => {
const { isConnected, data: account } = useAbstraxionAccount();

const contracts = searchParams.get("contracts");
const stake = Boolean(searchParams.get("stake"));
const bank = searchParams.get("bank");

let bankArray;
try {
bankArray = JSON.parse(bank || "");
} catch (e) {
// If the bank is not a valid JSON, we split it by comma. Dapp using old version of the library.
bankArray = [];
}

let contractsArray;
try {
contractsArray = JSON.parse(contracts || "");
Expand All @@ -59,8 +70,16 @@ export const Abstraxion = ({ isOpen, onClose }: ModalProps) => {
<DialogContent>
{abstraxionError ? (
<ErrorDisplay message={abstraxionError} onClose={onClose} />
) : account?.id && contracts && grantee ? (
<AbstraxionGrant contracts={contractsArray} grantee={grantee} />
) : account?.id &&
grantee &&
// We support granting any combunation of
(contractsArray.length > 0 || stake || bankArray.length > 0) ? (
<AbstraxionGrant
bank={bankArray}
contracts={contractsArray}
grantee={grantee}
stake={stake}
/>
) : isConnected ? (
<AbstraxionWallets />
) : (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MsgGrant } from "cosmjs-types/cosmos/authz/v1beta1/tx";
import { SendAuthorization } from "cosmjs-types/cosmos/bank/v1beta1/authz";

export const generateBankGrant = (
expiration: bigint,
grantee: string,
granter: string,
bank: { denom: string; amount: string }[],
) => {
return {
typeUrl: MsgGrant.typeUrl,
value: MsgGrant.fromPartial({
grant: {
authorization: {
typeUrl: SendAuthorization.typeUrl,
value: SendAuthorization.encode(
SendAuthorization.fromPartial({
spendLimit: bank,
}),
).finish(),
},
expiration: {
seconds: expiration,
},
},
grantee,
granter,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { ContractGrantDescription } from "@burnt-labs/abstraxion";
import {
AllowAllMessagesFilter,
CombinedLimit,
ContractExecutionAuthorization,
MaxCallsLimit,
} from "cosmjs-types/cosmwasm/wasm/v1/authz";
import { MsgGrant } from "cosmjs-types/cosmos/authz/v1beta1/tx";

export const generateContractGrant = (
expiration: bigint,
grantee: string,
granter: string,
contracts: ContractGrantDescription[],
) => {
const contractExecutionAuthorizationValue =
ContractExecutionAuthorization.encode(
ContractExecutionAuthorization.fromPartial({
grants: contracts.map((contractGrantDescription) => {
if (typeof contractGrantDescription === "string") {
const contract = contractGrantDescription;
return {
contract,
limit: {
typeUrl: MaxCallsLimit.typeUrl,
value: MaxCallsLimit.encode(
MaxCallsLimit.fromPartial({
remaining: BigInt("255"),
}),
).finish(),
},
filter: {
typeUrl: AllowAllMessagesFilter.typeUrl,
},
};
}

const { address, amounts } = contractGrantDescription;
return {
contract: address,
limit: {
typeUrl: "/cosmwasm.wasm.v1.CombinedLimit",
value: CombinedLimit.encode(
CombinedLimit.fromPartial({
callsRemaining: BigInt("255"),
amounts,
}),
).finish(),
},
filter: {
typeUrl: AllowAllMessagesFilter.typeUrl,
},
};
}),
}),
).finish();
const grantValue = MsgGrant.fromPartial({
grant: {
authorization: {
typeUrl: ContractExecutionAuthorization.typeUrl,
value: contractExecutionAuthorizationValue,
},
expiration: {
seconds: expiration,
},
},
grantee,
granter,
});

return {
typeUrl: MsgGrant.typeUrl,
value: grantValue,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { MsgGrant } from "cosmjs-types/cosmos/authz/v1beta1/tx";
import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz";
import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import {
AuthorizationType,
StakeAuthorization,
} from "cosmjs-types/cosmos/staking/v1beta1/authz";
import {
AllowedMsgAllowance,
BasicAllowance,
} from "cosmjs-types/cosmos/feegrant/v1beta1/feegrant";
import { MsgGrantAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/tx";

export const generateStakeGrant = (
expiration: bigint,
grantee: string,
granter: string,
): Array<{
typeUrl: string;
value: MsgGrant | MsgGrantAllowance;
}> => {
const feeGrant = {
typeUrl: MsgGrantAllowance.typeUrl,
value: MsgGrantAllowance.fromPartial({
allowance: {
typeUrl: AllowedMsgAllowance.typeUrl,
value: AllowedMsgAllowance.encode(
AllowedMsgAllowance.fromPartial({
allowance: {
typeUrl: BasicAllowance.typeUrl,
value: BasicAllowance.encode(
BasicAllowance.fromPartial({
spendLimit: [],
expiration: {
seconds: expiration,
},
}),
).finish(),
},
allowedMessages: [
StakeAuthorization.typeUrl,
MsgWithdrawDelegatorReward.typeUrl,
],
}),
).finish(),
},
grantee,
granter,
}),
};

// Need to grant MsgWithdrawDelegatorReward
const genericMsgGrant = {
typeUrl: MsgGrant.typeUrl,
value: MsgGrant.fromPartial({
grant: {
authorization: {
typeUrl: GenericAuthorization.typeUrl,
value: GenericAuthorization.encode(
GenericAuthorization.fromPartial({
msg: MsgWithdrawDelegatorReward.typeUrl,
}),
).finish(),
},
expiration: {
seconds: expiration,
},
},
grantee,
granter,
}),
};

const grants = [
AuthorizationType.AUTHORIZATION_TYPE_DELEGATE,
AuthorizationType.AUTHORIZATION_TYPE_UNDELEGATE,
AuthorizationType.AUTHORIZATION_TYPE_REDELEGATE,
].map((authorizationType) => ({
typeUrl: MsgGrant.typeUrl,
value: MsgGrant.fromPartial({
grant: {
authorization: {
typeUrl: StakeAuthorization.typeUrl,
value: StakeAuthorization.encode(
StakeAuthorization.fromPartial({
authorizationType,
}),
).finish(),
},
expiration: {
seconds: expiration,
},
},
grantee,
granter,
}),
}));

return [...grants, genericMsgGrant, feeGrant];
};
Loading

0 comments on commit 2c33c31

Please sign in to comment.