From Contracts to Map

Search for a command to run...

No comments yet. Be the first to comment.
Checkpoint SMB (Spark) offers built-in OpenTelemetry support, enabling users to export logs to OpenTelemetry-compatible servers for monitoring and visualization in tools like Grafana. Source: https://www.dash0.com/guides/opentelemetry-collector Impo...

In this post we’re exploring different ways to fetch live onchain data from different AAVE (V3) markets and display useful information in widgets on iOS. Why ? Aave V3 is a decentralized, non-custodial liquidity protocol that enables users to supply ...

Detecting impersonators on Telegram is a time-consuming task where you’re always one step behind — so why not automate it?

Flash loans have become one of the most debated mechanisms in the DeFi space. While they enable advanced trading strategies such as arbitrage and liquidation, they also create opportunities for exploits and market manipulation. In this article, we wi...

In this new article in our "Building On Alephium" series, we present how we developed the decentralized application "TokenFurnace" on Alephium.

This feature will be implemented in the next Rhône upgrade.
Map simplifies smart contract code for accessing and storing data. ALPH.bet utilizes this feature to manage data like player and round information more efficiently. This article will explain the changes made to adapt the smart contract to use Mapping.
Read our articles about the dapp we developed: part 1, part 2, and part 3.
Each time someone places a bet, a contract is deployed with details like the bet amount and specifics. Several functions utilize subcontracts to check if someone has already bet in a round or to withdraw the bet once it's completed.
The following code demonstrates how these functions were handled without Mapping:
Checking if someone already played:
assert!(!contractExists!(subContractId!(bidderContractId)), ErrorCodes.AlreadyPlayed)
Deploying a smart contract when someone bets:
let selfContractId = selfContractId!()
let (encodedImmFields, encodedMutFields) = PunterChoice.encodeFields!(selfContractId, caller, epoch, side, bidAmount, claimedByAnyoneTimestamp)
let _ = copyCreateSubContract!{caller -> ALPH: 1 alph}(bidderContractId, punterTemplateId, encodedImmFields, encodedMutFields)
Withdrawing:
let betInfoContractId = subContractId!(SubContractTypes.PunterChoice ++ toByteVec!(addressToClaim) ++ epochNumber)
let roundContractId = subContractId!(SubContractTypes.RoundMultipleChoice ++ epochNumber)
assert!(contractExists!(roundContractId), ErrorCodes.RoundNotExists)
assert!(contractExists!(betInfoContractId), ErrorCodes.PunterChoiceNotExists)
if (contractExists!(roundContractId) && contractExists!(betInfoContractId)){
let betInfoCaller = getBetInfoByEpoch(addressToClaim, epochNumber)
let addressPunterChoice = betInfoCaller.getAddress()
let canBeClaimedAt = betInfoCaller.getClaimedByAnyone()
if(addressToClaim != from){
assert!(timestampNow > canBeClaimedAt, ErrorCodes.CannotBeClaimedYet)
}
let amountPunterChoice = betInfoCaller.getAmountBid()
let upBid = betInfoCaller.getBid()
let round = getRoundByEpochByteVec(epochNumber)
round.userClaimRewards(addressToClaim, amountPunterChoice, upBid)
betInfoCaller.destroy(from)
}
Managing subcontracts can become complex with numerous contracts, which is where mapping becomes useful. This feature reduces complexity and provides a good abstraction.
Key-value pairs are fundamental in computing, linking an identifier (key) to a piece of data (value). For example, the key might be CITY, and the value might be BERLIN. Mapping is a set of key-value pairs.
For ALPH.bet, the major change was replacing contract management of players with a simple struct:
struct PunterChoice {
predictionContractId: ByteVec,
punterAddress: Address,
epoch: U256,
side: U256,
amountBid: U256,
claimedByAnyoneAt: U256
}
And adding this map at the beginning of the contract:
mapping[ByteVec, PunterChoice] punters
This simplifies checking if someone has already played:
assert!(!punters.contains!(bidderKey), ErrorCodes.AlreadyPlayed)
Previously, accessing player information involved computing the path and checking for subcontract existence:
fn getBetInfoByEpoch(from: Address, epochToGet: ByteVec) -> PunterChoice {
let bidderContractId = subContractId!(SubContractTypes.PunterChoice ++ toByteVec!(from) ++ epochToGet)
assert!(contractExists!(bidderContractId), ErrorCodes.PunterChoiceNotExists)
return PunterChoice(bidderContractId)
}
This has been replaced by one line, with the subcontract ID becoming the map key:
let betInfoContractKey = SubContractTypes.PunterChoice ++ toByteVec!(addressToClaim) ++ epochNumber
let betInfoCaller = punters[betInfoContractKey]
So when the refactor is done, the withdrawing function will look like this. The example below demonstrate how easy it is to access to player information and delete (=get back locked ALPH) the entry.
let roundContractId = subContractId!(SubContractTypes.RoundMultipleChoice ++ epochNumber)
let betInfoContractKey = SubContractTypes.PunterChoice ++ toByteVec!(addressToClaim) ++ epochNumber
assert!(contractExists!(roundContractId), ErrorCodes.RoundNotExists)
assert!(punters.contains!(betInfoContractKey), ErrorCodes.PunterChoiceNotExists)
if (contractExists!(roundContractId) && contractExists!(betInfoContractKey)){
let betInfoCaller = punters[betInfoContractKey]
let addressPunterChoice = betInfoCaller.punterAddress
let canBeClaimedAt = betInfoCaller.claimedByAnyoneAt
if(addressToClaim != from){
assert!(timestampNow > canBeClaimedAt, ErrorCodes.CannotBeClaimedYet)
}
let amountPunterChoice = betInfoCaller.amountBid
let upBid = betInfoCaller.side
let round = getRoundByEpochByteVec(epochNumber)
round.userClaimRewards(addressToClaim, amountPunterChoice, upBid)
punters.remove!(from, betInfoContractKey)
}
This new feature significantly aids in writing cleaner and more optimized code. The abstraction enhances the developer experience by simplifying the logic behind managing and deploying subcontracts.
Although the smart contract isn't much shorter, it is cleaner and greatly reduces the complexity of using subcontracts with Ralph.
This version maintains the technical detail while improving readability and emphasizing the benefits of the changes.