-
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.
feat: support configurable contants for predicates (#998)
- Loading branch information
1 parent
a17bc14
commit 7c8439f
Showing
14 changed files
with
489 additions
and
19 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,5 @@ | ||
--- | ||
"@fuel-ts/predicate": minor | ||
--- | ||
|
||
support configurable contants 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,5 @@ members = [ | |
"simple-token-abi", | ||
"echo-configurables", | ||
"transfer-to-address", | ||
"whitelisted-address-predicate", | ||
] |
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
7 changes: 7 additions & 0 deletions
7
apps/docs-snippets/contracts/whitelisted-address-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 = ["FuelLabs"] | ||
entry = "main.sw" | ||
license = "Apache-2.0" | ||
name = "whitelisted-address-predicate" | ||
|
||
[dependencies] |
11 changes: 11 additions & 0 deletions
11
apps/docs-snippets/contracts/whitelisted-address-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,11 @@ | ||
// #region predicates-with-configurable-constants-1 | ||
predicate; | ||
|
||
configurable { | ||
WHITELISTED: b256 = 0xa703b26833939dabc41d3fcaefa00e62cee8e1ac46db37e0fa5d4c9fe30b4132 | ||
} | ||
|
||
fn main(address: b256) -> bool { | ||
WHITELISTED == address | ||
} | ||
// #endregion predicates-with-configurable-constants-1 |
75 changes: 75 additions & 0 deletions
75
apps/docs-snippets/src/guide/predicates/predicates-with-configurable.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,75 @@ | ||
import { WalletUnlocked, Predicate, NativeAssetId, BN, getRandomB256 } from 'fuels'; | ||
|
||
import { SnippetContractEnum, getSnippetContractArtifacts } from '../../../contracts'; | ||
import { getTestWallet } from '../../utils'; | ||
|
||
describe(__filename, () => { | ||
let wallet: WalletUnlocked; | ||
|
||
const { abi, bin } = getSnippetContractArtifacts( | ||
SnippetContractEnum.WHITELISTED_ADDRESS_PREDICATE | ||
); | ||
|
||
beforeAll(async () => { | ||
wallet = await getTestWallet(); | ||
}); | ||
|
||
it('should successfully tranfer to setted whitelisted address', async () => { | ||
// #region predicates-with-configurable-constants-2 | ||
const newWhitelistedAddress = getRandomB256(); | ||
|
||
const configurable = { WHITELISTED: newWhitelistedAddress }; | ||
|
||
// instantiate predicate with configurable constants | ||
const predicate = new Predicate(bin, abi, wallet.provider, configurable); | ||
|
||
// set predicate data to be the same as the configurable constant | ||
predicate.setData(configurable.WHITELISTED); | ||
|
||
// transfering funds to the predicate | ||
const tx1 = await wallet.transfer(predicate.address, 500); | ||
|
||
await tx1.waitForResult(); | ||
|
||
const destinationWallet = WalletUnlocked.generate(); | ||
|
||
const amountToTransfer = 100; | ||
|
||
// transfering funds from the predicate to destination if predicate returns true | ||
const tx2 = await predicate.transfer(destinationWallet.address, amountToTransfer); | ||
|
||
await tx2.waitForResult(); | ||
// #endregion predicates-with-configurable-constants-2 | ||
|
||
const destinationBalance = await destinationWallet.getBalance(NativeAssetId); | ||
|
||
expect(new BN(destinationBalance).toNumber()).toEqual(amountToTransfer); | ||
}); | ||
|
||
it('should successfully tranfer to default whitelisted address', async () => { | ||
// #region predicates-with-configurable-constants-3 | ||
const predicate = new Predicate(bin, abi, wallet.provider); | ||
|
||
// set predicate data to be the same as the configurable constant | ||
predicate.setData('0xa703b26833939dabc41d3fcaefa00e62cee8e1ac46db37e0fa5d4c9fe30b4132'); | ||
|
||
// transfering funds to the predicate | ||
const tx1 = await wallet.transfer(predicate.address, 500); | ||
|
||
await tx1.waitForResult(); | ||
|
||
const destinationWallet = WalletUnlocked.generate(); | ||
|
||
const amountToTransfer = 100; | ||
|
||
// transfering funds from the predicate to destination if predicate returns true | ||
const tx2 = await predicate.transfer(destinationWallet.address, amountToTransfer); | ||
|
||
await tx2.waitForResult(); | ||
// #endregion predicates-with-configurable-constants-3 | ||
|
||
const destinationBalance = await destinationWallet.getBalance(NativeAssetId); | ||
|
||
expect(new BN(destinationBalance).toNumber()).toEqual(amountToTransfer); | ||
}); | ||
}); |
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
35 changes: 35 additions & 0 deletions
35
apps/docs/src/guide/predicates/predicates-with-configurable-constants.md
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,35 @@ | ||
# Predicates With Configurable Constants | ||
|
||
Predicates, [much like Contracts](../contracts/configurable-constants.md), support configurable constants. This enables Predicates to suit specific use cases and enhance their functionality. | ||
|
||
## Example: Asset Transfer Validation | ||
|
||
Let's consider an example where a Predicate is used to validate an asset transfer. In this case, the transfer will only be executed if the recipient's address is on a pre-approved whitelist. | ||
|
||
The following snippet illustrates how this could be implemented: | ||
|
||
<<< @/../../docs-snippets/contracts/whitelisted-address-predicate/src/main.sw#predicates-with-configurable-constants-1{rust:line-numbers} | ||
|
||
In this example, you'll notice the use of a configurable constant named `WHITELISTED`. This constant has a default value that represents the default approved address. | ||
|
||
## Modifying The Whitelist | ||
|
||
If there is a need to whitelist another address, the `WHITELISTED` constant can be easily updated. The following snippet demonstrates how to set a new value for the `WHITELISTED` constant and to make the Predicate execute the transfer: | ||
|
||
<<< @/../../docs-snippets/src/guide/predicates/predicates-with-configurable.test.ts#predicates-with-configurable-constants-2{ts:line-numbers} | ||
|
||
By ensuring that the updated `WHITELISTED` address matches the intended recipient's address, the Predicate will validate the transfer successfully. | ||
|
||
## Default Whitelist Address | ||
|
||
In scenarios where the default whitelisted address is already the intended recipient, there's no need to update the `WHITELISTED` constant. The Predicate will validate the transfer based on the default value. Here's how this scenario might look: | ||
|
||
<<< @/../../docs-snippets/src/guide/predicates/predicates-with-configurable.test.ts#predicates-with-configurable-constants-3{ts:line-numbers} | ||
|
||
This ability to configure constants within Predicates provides a flexible mechanism for customizing their behavior, thereby enhancing the robustness and versatility of our asset transfer process. | ||
|
||
It's important to note that these customizations do not directly modify the original Predicate. The address of a Predicate is a hash of its bytecode. Any change to the bytecode, including altering a constant value, would generate a different bytecode, and thus a different hash. This leads to the creation of a new Predicate with a new address. | ||
|
||
This doesn't mean that we're changing the behavior of the original Predicate. Instead, we're creating a new Predicate with a different configuration. | ||
|
||
Therefore, while configurable constants do indeed enhance the flexibility and robustness of Predicates, it is achieved by creating new Predicates with different configurations, rather than altering the behavior of existing ones. |
159 changes: 159 additions & 0 deletions
159
packages/fuel-gauge/src/predicate-with-configurable.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,159 @@ | ||
import { generateTestWallet } from '@fuel-ts/wallet/test-utils'; | ||
import { readFileSync } from 'fs'; | ||
import type { Account, CoinQuantityLike } from 'fuels'; | ||
import { getRandomB256, WalletUnlocked, Predicate, BN, NativeAssetId, Provider } from 'fuels'; | ||
import { join } from 'path'; | ||
|
||
import abi from '../test-projects/predicate-with-configurable/out/debug/predicate-with-configurable-abi.json'; | ||
|
||
const bytecode = readFileSync( | ||
join( | ||
__dirname, | ||
'../test-projects/predicate-with-configurable/out/debug/predicate-with-configurable.bin' | ||
) | ||
); | ||
|
||
const defaultValues = { | ||
FEE: 10, | ||
ADDRESS: '0x38966262edb5997574be45f94c665aedb41a1663f5b0528e765f355086eebf96', | ||
}; | ||
|
||
let wallet: WalletUnlocked; | ||
|
||
const fundPredicate = async (predicate: Predicate<[number]>, amount: number) => { | ||
const tx = await wallet.transfer(predicate.address, amount); | ||
|
||
await tx.waitForResult(); | ||
}; | ||
|
||
const assertAccountBalance = async (account: Account, valueToAssert: number) => { | ||
const balance = await account.getBalance(NativeAssetId); | ||
|
||
expect(new BN(balance).toNumber()).toEqual(valueToAssert); | ||
}; | ||
|
||
describe('Predicate With Configurable', () => { | ||
beforeAll(async () => { | ||
const provider = new Provider('http://127.0.0.1:4000/graphql'); | ||
|
||
const quantities: CoinQuantityLike[] = [ | ||
{ | ||
amount: 1_000_000, | ||
assetId: NativeAssetId, | ||
}, | ||
]; | ||
|
||
wallet = await generateTestWallet(provider, quantities); | ||
}); | ||
|
||
it('should assert when input values are set to default configurable constants values', async () => { | ||
const predicate = new Predicate(bytecode, abi, wallet.provider); | ||
|
||
const amountToTransfer = 200; | ||
|
||
// transfer funds to predicate | ||
await fundPredicate(predicate, 500); | ||
|
||
// create destination wallet | ||
const destination = WalletUnlocked.generate(); | ||
|
||
await assertAccountBalance(destination, 0); | ||
|
||
// set predicate input data to be the same as default configurable value | ||
predicate.setData(defaultValues.FEE, defaultValues.ADDRESS); | ||
|
||
const tx = await predicate.transfer(destination.address, amountToTransfer); | ||
|
||
await tx.waitForResult(); | ||
|
||
await assertAccountBalance(destination, amountToTransfer); | ||
}); | ||
|
||
it('should assert when input and configurable values are set equal (FEE)', async () => { | ||
const configurableConstants = { FEE: 35 }; | ||
|
||
expect(configurableConstants.FEE).not.toEqual(defaultValues.FEE); | ||
|
||
const predicate = new Predicate(bytecode, abi, wallet.provider, configurableConstants); | ||
|
||
const amountToTransfer = 300; | ||
|
||
const destination = WalletUnlocked.generate(); | ||
|
||
await assertAccountBalance(destination, 0); | ||
|
||
// transfer funds to predicate | ||
await fundPredicate(predicate, 500); | ||
|
||
predicate.setData(configurableConstants.FEE, defaultValues.ADDRESS); | ||
|
||
// executing predicate transfer | ||
const tx = await predicate.transfer(destination.address, amountToTransfer); | ||
|
||
await tx.waitForResult(); | ||
|
||
await assertAccountBalance(destination, amountToTransfer); | ||
}); | ||
|
||
it('should assert when input and configurable values are set equal (ADDRESS)', async () => { | ||
const configurableConstants = { ADDRESS: getRandomB256() }; | ||
|
||
expect(configurableConstants.ADDRESS).not.toEqual(defaultValues.ADDRESS); | ||
|
||
const predicate = new Predicate(bytecode, abi, wallet.provider, configurableConstants); | ||
|
||
const amountToTransfer = 300; | ||
|
||
const destination = WalletUnlocked.generate(); | ||
|
||
await assertAccountBalance(destination, 0); | ||
|
||
// transfer funds to predicate | ||
await fundPredicate(predicate, 500); | ||
|
||
predicate.setData(defaultValues.FEE, configurableConstants.ADDRESS); | ||
|
||
// executing predicate transfer | ||
const tx = await predicate.transfer(destination.address, amountToTransfer); | ||
|
||
await tx.waitForResult(); | ||
|
||
await assertAccountBalance(destination, amountToTransfer); | ||
}); | ||
|
||
it('should assert when input and configurable values are set equal (BOTH)', async () => { | ||
const configurableConstants = { | ||
FEE: 90, | ||
ADDRESS: getRandomB256(), | ||
}; | ||
|
||
expect(configurableConstants.FEE).not.toEqual(defaultValues.FEE); | ||
expect(configurableConstants.ADDRESS).not.toEqual(defaultValues.ADDRESS); | ||
|
||
const predicate = new Predicate(bytecode, abi, wallet.provider, configurableConstants); | ||
|
||
const amountToTransfer = 300; | ||
|
||
const destination = WalletUnlocked.generate(); | ||
|
||
await assertAccountBalance(destination, 0); | ||
|
||
await fundPredicate(predicate, 500); | ||
|
||
predicate.setData(configurableConstants.FEE, configurableConstants.ADDRESS); | ||
|
||
const tx = await predicate.transfer(destination.address, amountToTransfer); | ||
|
||
await tx.waitForResult(); | ||
|
||
await assertAccountBalance(destination, amountToTransfer); | ||
}); | ||
|
||
it('should throws when no input data is given', async () => { | ||
const predicate = new Predicate(bytecode, abi, wallet.provider); | ||
|
||
const destination = WalletUnlocked.generate(); | ||
|
||
await expect(predicate.transfer(destination.address, 300)).rejects.toThrowError(); | ||
}); | ||
}); |
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
5 changes: 5 additions & 0 deletions
5
packages/fuel-gauge/test-projects/predicate-with-configurable/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,5 @@ | ||
[project] | ||
license = "Apache-2.0" | ||
name = "predicate-with-configurable" | ||
|
||
[dependencies] |
10 changes: 10 additions & 0 deletions
10
packages/fuel-gauge/test-projects/predicate-with-configurable/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,10 @@ | ||
predicate; | ||
|
||
configurable { | ||
FEE: u8 = 10, | ||
ADDRESS: b256 = 0x38966262edb5997574be45f94c665aedb41a1663f5b0528e765f355086eebf96 | ||
} | ||
|
||
fn main(fee: u8, address: b256) -> bool { | ||
FEE == fee && address == ADDRESS | ||
} |
Oops, something went wrong.