Skip to content

Commit

Permalink
✨ implement decrease reserved supply feature (Carbonable#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
bal7hazar authored Aug 18, 2022
1 parent d25c74b commit d1bf33f
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 8 deletions.
3 changes: 3 additions & 0 deletions src/interfaces/minter.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ namespace ICarbonableMinter:
func set_unit_price(unit_price : Uint256):
end
func decrease_reserved_supply_for_mint(slots : Uint256):
end
func airdrop(to : felt, quantity : felt) -> (success : felt):
end
Expand Down
17 changes: 17 additions & 0 deletions src/mint/library.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,23 @@ namespace CarbonableMinter:
return ()
end

func decrease_reserved_supply_for_mint{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr
}(slots : Uint256):
alloc_locals

# Access control check
Ownable.assert_only_owner()
let (reserved_supply_for_mint) = reserved_supply_for_mint_.read()
let (enough_slots) = uint256_le(slots, reserved_supply_for_mint)
with_attr error_message("CarbonableMinter: not enough reserved slots"):
assert enough_slots = TRUE
end
let (new_reserved_supply_for_mint) = SafeUint256.sub_le(reserved_supply_for_mint, slots)
reserved_supply_for_mint_.write(new_reserved_supply_for_mint)
return ()
end
func airdrop{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
to : felt, quantity : felt
) -> (success : felt):
Expand Down
7 changes: 7 additions & 0 deletions src/mint/minter.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ func set_unit_price{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_chec
return CarbonableMinter.set_unit_price(unit_price)
end

@external
func decrease_reserved_supply_for_mint{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr
}(slots : Uint256):
return CarbonableMinter.decrease_reserved_supply_for_mint(slots)
end

@external
func airdrop{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
to : felt, quantity : felt
Expand Down
28 changes: 28 additions & 0 deletions tests/integrations/library.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ namespace carbonable_minter_instance:

# Externals

func decrease_reserved_supply_for_mint{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr, carbonable_minter : felt
}(slots : Uint256, caller : felt):
%{ stop_prank = start_prank(caller_address=ids.caller, target_contract_address=ids.carbonable_minter) %}
ICarbonableMinter.decrease_reserved_supply_for_mint(carbonable_minter, slots)
%{ stop_prank() %}
return ()
end

func withdraw{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr, carbonable_minter : felt
}(caller : felt) -> (success : felt):
Expand Down Expand Up @@ -352,6 +361,25 @@ namespace admin_instance:
return ()
end

func decrease_reserved_supply_for_mint{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr
}(slots : felt):
alloc_locals
let (carbonable_minter) = carbonable_minter_instance.deployed()
let (caller) = admin_instance.get_address()
let slots_uint256 = Uint256(slots, 0)
with carbonable_minter:
let (initial_supply) = carbonable_minter_instance.reserved_supply_for_mint()
carbonable_minter_instance.decrease_reserved_supply_for_mint(
slots=slots_uint256, caller=caller
)
let (returned_supply) = carbonable_minter_instance.reserved_supply_for_mint()
let (expected_supply) = SafeUint256.sub_le(initial_supply, slots_uint256)
assert returned_supply = expected_supply
end
return ()
end

func transferOwnership{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
newOwner : felt
):
Expand Down
9 changes: 7 additions & 2 deletions tests/integrations/test_nominal_case.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ func test_e2e_airdrop{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_ch
# - has enough funds: YES
# User: ADMIN
# - aidrop 5 nft to ANYONE
# - aidrop 4 nft to ANYONE
# - aidrop 3 nft to ANYONE
# - decrease reserved supply by one
# User: ADMIN
# - wants to buy 2 NFTs (2 public)
alloc_locals
let (anyone_address) = anyone.get_address()

Expand All @@ -154,7 +157,9 @@ func test_e2e_airdrop{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_ch
anyone.public_buy(quantity=2)
%{ expect_revert("TRANSACTION_FAILED", "CarbonableMinter: not enough available reserved NFTs") %}
admin.airdrop(to=anyone_address, quantity=5)
admin.airdrop(to=anyone_address, quantity=4)
admin.airdrop(to=anyone_address, quantity=3)
admin.decrease_reserved_supply_for_mint(slots=1)
anyone.public_buy(quantity=1)
admin.withdraw()

return ()
Expand Down
88 changes: 82 additions & 6 deletions tests/units/test_minter.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.uint256 import Uint256
from starkware.cairo.common.bool import TRUE, FALSE

from openzeppelin.security.safemath import SafeUint256

from src.mint.library import CarbonableMinter

const PROJECT_NFT_ADDRESS = 0x056d4ffea4ca664ffe1256af4b029998014471a87dec8036747a927ab3320b46
Expand Down Expand Up @@ -387,14 +389,12 @@ func test_airdrop_nominal_case{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*,
# Wants to aidrop 5 NFTs
# Whitelisted sale: OPEN
# Public sale: CLOSED
# Is user whitelisted: NO
# current NFT totalSupply: 5
# current NFT reserved supply: 0
# current NFT reserved supply: 5
%{ stop=start_prank(ids.context.signers.admin) %}
let quantity = 5
%{ mock_call(ids.context.mocks.project_nft_address, "totalSupply", [5, 0]) %}
%{ mock_call(ids.context.mocks.project_nft_address, "mint", []) %}
CarbonableMinter.airdrop(to=context.signers.anyone_1, quantity=quantity)
CarbonableMinter.airdrop(to=context.signers.anyone_1, quantity=5)
%{ stop() %}
return ()
end
Expand Down Expand Up @@ -437,17 +437,93 @@ func test_airdrop_revert_not_enough_nfts_available{
# current NFT reserved supply: 1
# has enough funds: YES
%{ stop=start_prank(ids.context.signers.admin) %}
let quantity = 5
%{ mock_call(ids.context.mocks.project_nft_address, "totalSupply", [6, 0]) %}
%{ expect_revert("TRANSACTION_FAILED", "CarbonableMinter: not enough available NFTs") %}
CarbonableMinter.airdrop(to=context.signers.anyone_1, quantity=quantity)
CarbonableMinter.airdrop(to=context.signers.anyone_1, quantity=5)
CarbonableMinter.airdrop(to=context.signers.anyone_1, quantity=1)
%{ expect_revert("TRANSACTION_FAILED", "CarbonableMinter: not enough available reserved NFTs") %}
CarbonableMinter.airdrop(to=context.signers.anyone_1, quantity=1)
%{ stop() %}
return ()
end

@external
func test_decrease_reserved_supply_nominal_case{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr
}():
alloc_locals
let unit_price = Uint256(10, 0)
let max_supply = Uint256(10, 0)
let reserved_supply = Uint256(5, 0)
let (local context : TestContext) = test_internal.prepare(
1, FALSE, 5, unit_price, max_supply, reserved_supply
)

# User: admin
# Wants to decrease the reserved supply by 2
# Whitelisted sale: OPEN
# Public sale: CLOSED
# current NFT reserved supply: 5
%{ stop=start_prank(ids.context.signers.admin) %}
let slots = Uint256(2, 0)
let (expected_slots) = SafeUint256.sub_le(reserved_supply, slots)
CarbonableMinter.decrease_reserved_supply_for_mint(slots=slots)
let (returned_supply) = CarbonableMinter.reserved_supply_for_mint()
assert returned_supply = expected_slots
%{ stop() %}
return ()
end

@external
func test_decrease_reserved_supply_revert_not_owner{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr
}():
alloc_locals
let unit_price = Uint256(10, 0)
let max_supply = Uint256(10, 0)
let reserved_supply = Uint256(5, 0)
let (local context : TestContext) = test_internal.prepare(
1, FALSE, 5, unit_price, max_supply, reserved_supply
)

# User: anyone_1
# Wants to decrease the reserved supply by 2
# Whitelisted sale: OPEN
# Public sale: CLOSED
# current NFT reserved supply: 5
%{ stop=start_prank(ids.context.signers.anyone_1) %}
let slots = Uint256(2, 0)
%{ expect_revert("TRANSACTION_FAILED", "Ownable: caller is not the owner") %}
CarbonableMinter.decrease_reserved_supply_for_mint(slots=slots)
%{ stop() %}
return ()
end

@external
func test_decrease_reserved_supply_revert_over_decreased{
syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr
}():
alloc_locals
let unit_price = Uint256(10, 0)
let max_supply = Uint256(10, 0)
let reserved_supply = Uint256(5, 0)
let (local context : TestContext) = test_internal.prepare(
1, FALSE, 5, unit_price, max_supply, reserved_supply
)

# User: admin
# Wants to decrease the reserved supply by 6
# Whitelisted sale: OPEN
# Public sale: CLOSED
# current NFT reserved supply: 5
%{ stop=start_prank(ids.context.signers.admin) %}
let slots = Uint256(6, 0)
%{ expect_revert("TRANSACTION_FAILED", "CarbonableMinter: not enough reserved slots") %}
CarbonableMinter.decrease_reserved_supply_for_mint(slots=slots)
%{ stop() %}
return ()
end

@external
func test_withdraw_nominal_case{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(
):
Expand Down

0 comments on commit d1bf33f

Please sign in to comment.