-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: add more thorough examples for predicates (#2345)
Co-authored-by: Anderson Arboleya <[email protected]> Co-authored-by: Daniel Bate <[email protected]> Co-authored-by: Nedim Salkić <[email protected]> Co-authored-by: Peter Smith <[email protected]> Co-authored-by: Sérgio Torres <[email protected]> Co-authored-by: Dhaiwat <[email protected]>
- Loading branch information
1 parent
a7a7866
commit 1e7e4d8
Showing
19 changed files
with
481 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
--- | ||
--- | ||
|
||
docs: add more thorough examples for predicates |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
183 changes: 183 additions & 0 deletions
183
apps/create-fuels-counter-guide/src/pages/predicate.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
import { Button } from "@/components/Button"; | ||
import { FuelLogo } from "@/components/FuelLogo"; | ||
import { Input } from "@/components/Input"; | ||
import { Link } from "@/components/Link"; | ||
import { useActiveWallet } from "@/hooks/useActiveWallet"; | ||
/** @knipignore */ | ||
import { TestPredicateAbi__factory } from "@/sway-api"; | ||
import { BN, InputValue, Predicate } from "fuels"; | ||
import { bn } from "fuels"; | ||
import { useState } from "react"; | ||
import toast from "react-hot-toast"; | ||
import useAsync from "react-use/lib/useAsync"; | ||
|
||
export default function PredicateExample() { | ||
let baseAssetId: string; | ||
|
||
const { wallet, walletBalance, refreshWalletBalance } = useActiveWallet(); | ||
|
||
const [predicate, setPredicate] = useState<Predicate<InputValue[]>>(); | ||
|
||
const [predicateBalance, setPredicateBalance] = useState<BN>(); | ||
|
||
const [pin, setPin] = useState<string>(); | ||
|
||
useAsync(async () => { | ||
if (wallet) { | ||
baseAssetId = wallet.provider.getBaseAssetId(); | ||
const predicate = TestPredicateAbi__factory.createInstance(wallet.provider); | ||
setPredicate(predicate); | ||
setPredicateBalance(await predicate.getBalance()); | ||
} | ||
}, [wallet]); | ||
|
||
const refreshBalances = async () => { | ||
await refreshWalletBalance?.(); | ||
setPredicateBalance(await predicate?.getBalance()); | ||
}; | ||
|
||
const transferFundsToPredicate = async (amount: BN) => { | ||
if (!predicate) { | ||
return toast.error("Predicate not loaded"); | ||
} | ||
|
||
if (!wallet) { | ||
return toast.error("Wallet not loaded"); | ||
} | ||
|
||
await wallet.transfer(predicate.address, amount, baseAssetId, { | ||
gasLimit: 10_000, | ||
}); | ||
|
||
await refreshBalances(); | ||
|
||
return toast.success("Funds transferred to predicate."); | ||
}; | ||
|
||
const unlockPredicateAndTransferFundsBack = async (amount: BN) => { | ||
try { | ||
if (!wallet) { | ||
return toast.error("Wallet not loaded"); | ||
} | ||
|
||
const reInitializePredicate = TestPredicateAbi__factory.createInstance(wallet.provider, [bn(pin)]); | ||
|
||
if (!reInitializePredicate) { | ||
return toast.error("Failed to initialize predicate"); | ||
} | ||
|
||
const tx = await reInitializePredicate.transfer(wallet.address, amount, baseAssetId); | ||
const { isStatusSuccess } = await tx.wait(); | ||
|
||
if (!isStatusSuccess) { | ||
toast.error("Failed to unlock predicate"); | ||
return; | ||
} | ||
|
||
if (isStatusSuccess) { | ||
toast.success("Predicate unlocked"); | ||
} | ||
|
||
await refreshBalances(); | ||
} catch (e) { | ||
console.error(e); | ||
toast.error( | ||
"Failed to unlock predicate. You probably entered the wrong pin, or the predicate does not have enough balance. Try again." | ||
); | ||
} | ||
}; | ||
|
||
// #region change-pin-react-function | ||
const changePin = async () => { | ||
if (!wallet) { | ||
return toast.error("Wallet not loaded"); | ||
} | ||
if (!predicate) { | ||
return toast.error("Predicate not loaded"); | ||
} | ||
|
||
if (walletBalance?.eq(0)) { | ||
return toast.error( | ||
"Your wallet does not have enough funds. Please click the 'Top-up Wallet' button in the top right corner, or use the local faucet." | ||
); | ||
} | ||
|
||
if (!pin) { | ||
return toast.error("Please enter a pin"); | ||
} | ||
|
||
const configurable = { PIN: bn(pin) }; | ||
// instantiate predicate with configurable constants | ||
const reInitializePredicate = TestPredicateAbi__factory.createInstance(wallet.provider, [bn(configurable.PIN)], configurable); | ||
|
||
if (!reInitializePredicate) { | ||
return toast.error("Failed to initialize predicate"); | ||
} | ||
|
||
// transferring funds to the predicate | ||
const tx = await wallet.transfer(reInitializePredicate.address, 1000, baseAssetId, { | ||
gasLimit: 10_000, | ||
}); | ||
|
||
const { isStatusSuccess } = await tx.wait(); | ||
|
||
if (!isStatusSuccess) { | ||
toast.error("Failed to update pin in predicate"); | ||
return; | ||
} | ||
|
||
if (isStatusSuccess) { | ||
toast.success("Predicate pin updated"); | ||
} | ||
|
||
await refreshWalletBalance?.(); | ||
}; | ||
// #endregion change-pin-react-function | ||
|
||
return ( | ||
<> | ||
<div className="flex gap-4"> | ||
<FuelLogo /> | ||
<h3 className="text-2xl font-semibold">Predicate</h3> | ||
</div> | ||
|
||
<div className="mt-12 items-baseline flex gap-2"> | ||
<h5 className="font-semibold text-xl">Wallet Balance:</h5> | ||
<span className="text-gray-400">{walletBalance?.toString()}</span> | ||
</div> | ||
|
||
<div className="items-baseline flex gap-2"> | ||
<h5 className="font-semibold text-xl">Predicate Balance:</h5> | ||
<span className="text-gray-400">{predicateBalance?.toString()}</span> | ||
</div> | ||
|
||
<Button onClick={async () => await transferFundsToPredicate(bn(1000))}>Transfer 1000 to Predicate</Button> | ||
|
||
<Button onClick={changePin}>Change Pin</Button> | ||
|
||
<Input | ||
className="w-[300px] mt-8" | ||
value={pin as string} | ||
onChange={(e) => setPin(e.target.value)} | ||
placeholder="Enter a new pin" | ||
/> | ||
|
||
<Button onClick={async () => await unlockPredicateAndTransferFundsBack(bn(1000))}> | ||
Unlock Predicate and Transfer 1000 back to Wallet | ||
</Button> | ||
|
||
<span className="mt-8 w-[400px] text-gray-400"> | ||
Do note that when you 'unlock' a predicate, the predicate also pays for the gas of the transaction. <br /> | ||
This is why you will notice that the balance of the predicate gets reduced by 1000 + a nominal gas fee. | ||
</span> | ||
|
||
<Link href="https://docs.fuel.network/docs/intro/glossary/#predicate" target="_blank"> | ||
Learn more about Predicates | ||
</Link> | ||
|
||
<Link href="/" className="mt-12"> | ||
Back to Home | ||
</Link> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
[workspace] | ||
members = ["contract"] | ||
members = ["contract", "predicate"] |
2 changes: 2 additions & 0 deletions
2
apps/create-fuels-counter-guide/sway-programs/predicate/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
out | ||
target |
7 changes: 7 additions & 0 deletions
7
apps/create-fuels-counter-guide/sway-programs/predicate/Forc.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[project] | ||
authors = ["Fuel Labs <[email protected]>"] | ||
entry = "main.sw" | ||
license = "Apache-2.0" | ||
name = "test-predicate" | ||
|
||
[dependencies] |
9 changes: 9 additions & 0 deletions
9
apps/create-fuels-counter-guide/sway-programs/predicate/src/main.sw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
predicate; | ||
|
||
configurable { | ||
PIN: u64 = 1337, | ||
} | ||
|
||
fn main(pin: u64) -> bool { | ||
return PIN == pin; | ||
} |
122 changes: 122 additions & 0 deletions
122
apps/docs-snippets/src/guide/predicates/interacting-with-predicates.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { seedTestWallet } from '@fuel-ts/account/test-utils'; | ||
import type { Provider, WalletUnlocked } from 'fuels'; | ||
import { ScriptTransactionRequest, bn, Predicate, BN } from 'fuels'; | ||
|
||
import { | ||
DocSnippetProjectsEnum, | ||
getDocsSnippetsForcProject, | ||
} from '../../../test/fixtures/forc-projects'; | ||
import { getTestWallet } from '../../utils'; | ||
|
||
/** | ||
* @group node | ||
*/ | ||
describe(__filename, () => { | ||
let wallet: WalletUnlocked; | ||
let receiver: WalletUnlocked; | ||
let baseAssetId: string; | ||
let provider: Provider; | ||
let predicate: Predicate<[string]>; | ||
|
||
const { abiContents: abi, binHexlified: bin } = getDocsSnippetsForcProject( | ||
DocSnippetProjectsEnum.SIMPLE_PREDICATE | ||
); | ||
|
||
const inputAddress = '0xfc05c23a8f7f66222377170ddcbfea9c543dff0dd2d2ba4d0478a4521423a9d4'; | ||
|
||
beforeAll(async () => { | ||
wallet = await getTestWallet(); | ||
receiver = await getTestWallet(); | ||
provider = wallet.provider; | ||
|
||
baseAssetId = wallet.provider.getBaseAssetId(); | ||
|
||
predicate = new Predicate<[string]>({ | ||
bytecode: bin, | ||
provider: wallet.provider, | ||
abi, | ||
inputData: [inputAddress], | ||
}); | ||
await seedTestWallet(predicate, [[100_000_000, baseAssetId]]); | ||
}); | ||
|
||
it('should get predicate resources and add them to the predicate data', async () => { | ||
// #region interacting-with-predicates-1 | ||
|
||
// Instantiate the transaction request | ||
const transactionRequest = new ScriptTransactionRequest({ | ||
gasLimit: 2000, | ||
maxFee: bn(0), | ||
}); | ||
|
||
const predicateCoins = await predicate.getResourcesToSpend([ | ||
{ amount: 2000, assetId: baseAssetId }, | ||
]); | ||
|
||
// Add the predicate input and resources | ||
transactionRequest.addResources(predicateCoins); | ||
// #endregion interacting-with-predicates-1 | ||
|
||
expect(transactionRequest.inputs.length).toBeGreaterThanOrEqual(1); | ||
expect(transactionRequest.outputs.length).toEqual(1); | ||
}); | ||
|
||
it('should successfully transfer funds to the predicate', async () => { | ||
const transactionRequest = new ScriptTransactionRequest({ gasLimit: 2000, maxFee: bn(0) }); | ||
transactionRequest.addCoinOutput(receiver.address, 100, baseAssetId); | ||
|
||
const txCost = await provider.getTransactionCost(transactionRequest, { | ||
resourcesOwner: predicate, | ||
}); | ||
|
||
transactionRequest.gasLimit = txCost.gasUsed; | ||
transactionRequest.maxFee = txCost.maxFee; | ||
|
||
await predicate.fund(transactionRequest, txCost); | ||
|
||
// #region interacting-with-predicates-2 | ||
|
||
const result = await predicate.sendTransaction(transactionRequest); | ||
|
||
await result.waitForResult(); | ||
// #endregion interacting-with-predicates-2 | ||
|
||
const { isStatusSuccess } = await result.waitForResult(); | ||
|
||
expect(isStatusSuccess).toBeTruthy(); | ||
}); | ||
|
||
it('should successfully simulate a transaction with predicate', async () => { | ||
// #region interacting-with-predicates-3 | ||
const transactionRequest = new ScriptTransactionRequest({ gasLimit: 2000, maxFee: bn(0) }); | ||
transactionRequest.addCoinOutput(receiver.address, 1000000, baseAssetId); | ||
|
||
const txCost = await provider.getTransactionCost(transactionRequest, { | ||
resourcesOwner: predicate, | ||
}); | ||
|
||
transactionRequest.gasLimit = txCost.gasUsed; | ||
transactionRequest.maxFee = txCost.maxFee; | ||
|
||
await predicate.fund(transactionRequest, txCost); | ||
|
||
const result = await predicate.simulateTransaction(transactionRequest); | ||
|
||
// #endregion interacting-with-predicates-3 | ||
|
||
expect(result.receipts).toEqual([ | ||
{ | ||
type: 1, | ||
id: expect.any(String), | ||
val: expect.any(BN), | ||
pc: expect.any(BN), | ||
is: expect.any(BN), | ||
}, | ||
{ | ||
type: 9, | ||
gasUsed: expect.any(BN), | ||
result: expect.any(BN), | ||
}, | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,4 +33,5 @@ members = [ | |
"script-signing", | ||
"input-output-types", | ||
"bytecode-input", | ||
"configurable-pin", | ||
] |
7 changes: 7 additions & 0 deletions
7
apps/docs-snippets/test/fixtures/forc-projects/configurable-pin/Forc.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[project] | ||
authors = ["Fuel Labs <[email protected]>"] | ||
entry = "main.sw" | ||
license = "Apache-2.0" | ||
name = "configurable-pin" | ||
|
||
[dependencies] |
12 changes: 12 additions & 0 deletions
12
apps/docs-snippets/test/fixtures/forc-projects/configurable-pin/src/main.sw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// #region predicate-with-configurable-pin-1 | ||
predicate; | ||
|
||
configurable { | ||
PIN: u64 = 1337, | ||
} | ||
|
||
fn main(pin: u64) -> bool { | ||
return PIN == pin; | ||
} | ||
|
||
// #endregion predicate-with-configurable-pin-1 |
Oops, something went wrong.