From f731db3eb2ff7e018b3d63f2a9703c65ecafd0d6 Mon Sep 17 00:00:00 2001
From: 0xTopaz <myunghwan@onbloc.xyz>
Date: Tue, 15 Oct 2024 18:39:54 +0900
Subject: [PATCH 1/2] fix : sync with gnoswap main branch

- last commit of gnoswap is 3361e994757821a312de2340474988e649e5a9c4
---
 .../gnoswap/v2/launchpad/_RPC_api_project.gno |  82 ++++++++
 .../v2/launchpad/__TEST_RPC_api_test.gnoA     |  36 ++++
 ...e_deposit_reward_by_proejct_tier_test.gnoA |  42 ++--
 ...e_deposit_01_deposit_collect_gns_test.gnoA |  24 +--
 ...two_tier_two_deposit_collect_gns_test.gnoA | 179 +++++++++++++++++
 .../v2/launchpad/launchpad_deposit.gno        | 178 ++++++++++++++++-
 .../r/gnoswap/v2/launchpad/launchpad_init.gno |   5 +-
 .../gnoswap/v2/launchpad/launchpad_reward.gno | 187 +-----------------
 .../gno.land/r/gnoswap/v2/launchpad/type.gno  |  10 +-
 9 files changed, 515 insertions(+), 228 deletions(-)
 create mode 100644 examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_two_project_two_tier_two_deposit_collect_gns_test.gnoA

diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/_RPC_api_project.gno b/examples/gno.land/r/gnoswap/v2/launchpad/_RPC_api_project.gno
index ad163141f61..d3364f618c2 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/_RPC_api_project.gno
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/_RPC_api_project.gno
@@ -8,6 +8,46 @@ import (
 	"gno.land/p/demo/ufmt"
 )
 
+func ApiGetProjectAndTierStatisticsByProjectId(projectId string) string {
+	project, exist := projects[projectId]
+	if !exist {
+		return ""
+	}
+
+	totalDepositAmount := project.totalDepositAmount
+	actualDepositAmount := project.actualDepositAmount
+
+	totalParticipant := project.totalParticipant
+	actualParticipant := project.actualParticipant
+
+	totalCollectedAmount := project.totalCollectedAmount
+
+	projectObj := metaNode()
+	projectObj.AppendObject("projectId", json.StringNode("projectId", projectId))
+	projectObj.AppendObject("totalDepositAmount", json.StringNode("totalDepositAmount", ufmt.Sprintf("%d", totalDepositAmount)))
+	projectObj.AppendObject("actualDepositAmount", json.StringNode("actualDepositAmount", ufmt.Sprintf("%d", actualDepositAmount)))
+	projectObj.AppendObject("totalParticipant", json.StringNode("totalParticipant", ufmt.Sprintf("%d", totalParticipant)))
+	projectObj.AppendObject("actualParticipant", json.StringNode("actualParticipant", ufmt.Sprintf("%d", actualParticipant)))
+	projectObj.AppendObject("totalCollectedAmount", json.StringNode("totalCollectedAmount", ufmt.Sprintf("%d", totalCollectedAmount)))
+
+	projectObj.AppendObject("tier30TotalDepositAmount", json.StringNode("tier30TotalDepositAmount", ufmt.Sprintf("%d", project.tier30.totalDepositAmount)))
+	projectObj.AppendObject("tier30ActualDepositAmount", json.StringNode("tier30ActualDepositAmount", ufmt.Sprintf("%d", project.tier30.actualDepositAmount)))
+	projectObj.AppendObject("tier30TotalParticipant", json.StringNode("tier30TotalParticipant", ufmt.Sprintf("%d", project.tier30.totalParticipant)))
+	projectObj.AppendObject("tier30ActualParticipant", json.StringNode("tier30ActualParticipant", ufmt.Sprintf("%d", project.tier30.actualParticipant)))
+
+	projectObj.AppendObject("tier90TotalDepositAmount", json.StringNode("tier90TotalDepositAmount", ufmt.Sprintf("%d", project.tier90.totalDepositAmount)))
+	projectObj.AppendObject("tier90ActualDepositAmount", json.StringNode("tier90ActualDepositAmount", ufmt.Sprintf("%d", project.tier90.actualDepositAmount)))
+	projectObj.AppendObject("tier90TotalParticipant", json.StringNode("tier90TotalParticipant", ufmt.Sprintf("%d", project.tier90.totalParticipant)))
+	projectObj.AppendObject("tier90ActualParticipant", json.StringNode("tier90ActualParticipant", ufmt.Sprintf("%d", project.tier90.actualParticipant)))
+
+	projectObj.AppendObject("tier180TotalDepositAmount", json.StringNode("tier180TotalDepositAmount", ufmt.Sprintf("%d", project.tier180.totalDepositAmount)))
+	projectObj.AppendObject("tier180ActualDepositAmount", json.StringNode("tier180ActualDepositAmount", ufmt.Sprintf("%d", project.tier180.actualDepositAmount)))
+	projectObj.AppendObject("tier180TotalParticipant", json.StringNode("tier180TotalParticipant", ufmt.Sprintf("%d", project.tier180.totalParticipant)))
+	projectObj.AppendObject("tier180ActualParticipant", json.StringNode("tier180ActualParticipant", ufmt.Sprintf("%d", project.tier180.actualParticipant)))
+
+	return marshal(projectObj)
+}
+
 func ApiGetProjectStatisticsByProjectId(projectId string) string {
 	project, exist := projects[projectId]
 	if !exist {
@@ -23,6 +63,7 @@ func ApiGetProjectStatisticsByProjectId(projectId string) string {
 	totalCollectedAmount := project.totalCollectedAmount
 
 	projectObj := metaNode()
+	projectObj.AppendObject("projectId", json.StringNode("projectId", projectId))
 	projectObj.AppendObject("totalDepositAmount", json.StringNode("totalDepositAmount", ufmt.Sprintf("%d", totalDepositAmount)))
 	projectObj.AppendObject("actualDepositAmount", json.StringNode("actualDepositAmount", ufmt.Sprintf("%d", actualDepositAmount)))
 	projectObj.AppendObject("totalParticipant", json.StringNode("totalParticipant", ufmt.Sprintf("%d", totalParticipant)))
@@ -32,6 +73,47 @@ func ApiGetProjectStatisticsByProjectId(projectId string) string {
 	return marshal(projectObj)
 }
 
+func ApiGetProjectStatisticsByProjectTierId(tierId string) string {
+	projectId, tierStr := getProjectIdAndTierFromTierId(tierId)
+	project, exist := projects[projectId]
+	if !exist {
+		println("NO PROJECT FOR THIS ID", projectId)
+		return ""
+	}
+
+	var tier Tier
+	switch tierStr {
+	case "30":
+		tier = project.tier30
+	case "90":
+		tier = project.tier90
+	case "180":
+		tier = project.tier180
+	default:
+		println("NO TIER FOR THIS ID", tierId)
+		return ""
+	}
+
+	tierAmount := tier.tierAmount // project token allocation
+
+	tierTotalDepositAmount := tier.totalDepositAmount
+	tierActualDepositAmount := tier.actualDepositAmount
+
+	tierTotalParticipant := tier.totalParticipant
+	tierActualParticipant := tier.actualParticipant
+
+	projectTierObj := metaNode()
+	projectTierObj.AppendObject("projectId", json.StringNode("projectId", projectId))
+	projectTierObj.AppendObject("tierId", json.StringNode("tierId", tierId))
+	projectTierObj.AppendObject("tierAmount", json.StringNode("tierAmount", ufmt.Sprintf("%d", tierAmount)))
+	projectTierObj.AppendObject("tierTotalDepositAmount", json.StringNode("tierTotalDepositAmount", ufmt.Sprintf("%d", tierTotalDepositAmount)))
+	projectTierObj.AppendObject("tierActualDepositAmount", json.StringNode("tierActualDepositAmount", ufmt.Sprintf("%d", tierActualDepositAmount)))
+	projectTierObj.AppendObject("tierTotalParticipant", json.StringNode("tierTotalParticipant", ufmt.Sprintf("%d", tierTotalParticipant)))
+	projectTierObj.AppendObject("tierActualParticipant", json.StringNode("tierActualParticipant", ufmt.Sprintf("%d", tierActualParticipant)))
+
+	return marshal(projectTierObj)
+}
+
 func metaNode() *json.Node {
 	height := std.GetHeight()
 	now := time.Now().Unix()
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_RPC_api_test.gnoA b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_RPC_api_test.gnoA
index 9a2be941925..c726dda2b97 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_RPC_api_test.gnoA
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_RPC_api_test.gnoA
@@ -199,3 +199,39 @@ func TestApiGetClaimableDepositByAddress(t *testing.T) {
 		shouldEQ(t, claimed, uint64(0))
 	})
 }
+
+func TestApiGetProjectAndTierStatisticsByProjectId(t *testing.T) {
+	t.Run("not existing project", func(t *testing.T) {
+		got := ApiGetProjectAndTierStatisticsByProjectId("gno.land/r")
+		shouldEQ(t, got, ``)
+	})
+
+	t.Run("existing project", func(t *testing.T) {
+		got := ApiGetProjectAndTierStatisticsByProjectId("gno.land/r/onbloc/obl:124")
+		shouldEQ(t, got, `{"height":"1296142","now":"1237159928","projectId":"gno.land/r/onbloc/obl:124","totalDepositAmount":"1000000","actualDepositAmount":"0","totalParticipant":"1","actualParticipant":"0","totalCollectedAmount":"0","tier30TotalDepositAmount":"1000000","tier30ActualDepositAmount":"0","tier30TotalParticipant":"1","tier30ActualParticipant":"0","tier90TotalDepositAmount":"0","tier90ActualDepositAmount":"0","tier90TotalParticipant":"0","tier90ActualParticipant":"0","tier180TotalDepositAmount":"0","tier180ActualDepositAmount":"0","tier180TotalParticipant":"0","tier180ActualParticipant":"0"}`)
+	})
+}
+
+func TestApiGetProjectStatisticsByProjectId(t *testing.T) {
+	t.Run("not existing project", func(t *testing.T) {
+		got := ApiGetProjectStatisticsByProjectId("gno.land/r")
+		shouldEQ(t, got, ``)
+	})
+
+	t.Run("existing project", func(t *testing.T) {
+		got := ApiGetProjectStatisticsByProjectId("gno.land/r/onbloc/obl:124")
+		shouldEQ(t, got, `{"height":"1296142","now":"1237159928","projectId":"gno.land/r/onbloc/obl:124","totalDepositAmount":"1000000","actualDepositAmount":"0","totalParticipant":"1","actualParticipant":"0","totalCollectedAmount":"0"}`)
+	})
+}
+
+func TestApiGetProjectStatisticsByProjectTierId(t *testing.T) {
+	t.Run("not existing tier", func(t *testing.T) {
+		got := ApiGetProjectStatisticsByProjectTierId("gno.land/r/onbloc/obl:124:31")
+		shouldEQ(t, got, ``)
+	})
+
+	t.Run("existing tier", func(t *testing.T) {
+		got := ApiGetProjectStatisticsByProjectTierId("gno.land/r/onbloc/obl:124:30")
+		shouldEQ(t, got, `{"height":"1296142","now":"1237159928","projectId":"gno.land/r/onbloc/obl:124","tierId":"gno.land/r/onbloc/obl:124:30","tierAmount":"100000000","tierTotalDepositAmount":"1000000","tierActualDepositAmount":"0","tierTotalParticipant":"1","tierActualParticipant":"0"}`)
+	})
+}
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier180_single_deposit_reward_by_proejct_tier_test.gnoA b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier180_single_deposit_reward_by_proejct_tier_test.gnoA
index 237250854c1..2e662f965b4 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier180_single_deposit_reward_by_proejct_tier_test.gnoA
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier180_single_deposit_reward_by_proejct_tier_test.gnoA
@@ -49,7 +49,7 @@ func TestCreateProject(t *testing.T) {
 	std.TestSkipHeights(1)
 }
 
-func TestDepositGnsToTier90(t *testing.T) {
+func TestDepositGnsToTier180(t *testing.T) {
 	std.TestSetRealm(gsaRealm)
 	gns.Transfer(a2u(user01), uint64(1_000_000)) // to deposit
 	// transfer some grc20 tokens to bypass project condition
@@ -61,59 +61,59 @@ func TestDepositGnsToTier90(t *testing.T) {
 
 	// skip some blocks to make project active
 	std.TestSkipHeights(4)
-	depositId := DepositGns("gno.land/r/onbloc/obl:124:90", uint64(1_000_000))
-	shouldEQ(t, depositId, `gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+	depositId := DepositGns("gno.land/r/onbloc/obl:124:180", uint64(1_000_000))
+	shouldEQ(t, depositId, `gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
 	std.TestSkipHeights(1)
 }
 
 func TestCollectRewardByDepositId(t *testing.T) {
 	std.TestSetRealm(user01Realm)
 
-	t.Run("claim reward before 7 days(for 90day tier's init reward)", func(t *testing.T) {
-		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+	t.Run("claim reward before 14 days(for 180day tier's init reward)", func(t *testing.T) {
+		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
 		shouldEQ(t, reward, uint64(0))
 		std.TestSkipHeights(1)
 		println()
 	})
 
-	t.Run("claim reward before 7 days(for 90day tier's init reward)", func(t *testing.T) {
+	t.Run("claim reward before 14 days(for 180day tier's init reward)", func(t *testing.T) {
 		std.TestSkipHeights(123)
-		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
 		shouldEQ(t, reward, uint64(0))
 		std.TestSkipHeights(1)
 		println()
 	})
 
-	t.Run("claim after 7 days", func(t *testing.T) {
-		std.TestSkipHeights(int64(TIMESTAMP_7DAYS) / 2)
-		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
-		shouldEQ(t, reward, uint64(15562035))
+	t.Run("claim after 14 days", func(t *testing.T) {
+		std.TestSkipHeights(int64(TIMESTAMP_14DAYS) / 2)
+		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+		shouldEQ(t, reward, uint64(54455786))
 		println()
 	})
 
 	t.Run("no more claim in same block", func(t *testing.T) {
-		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
 		shouldEQ(t, reward, uint64(0))
 		println()
 	})
 
 	t.Run("wait 1 more block, then claim", func(t *testing.T) {
 		std.TestSkipHeights(1)
-		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
-		shouldEQ(t, reward, uint64(51))
+		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+		shouldEQ(t, reward, uint64(90))
 		println()
 	})
 
-	t.Run("90day tier is over", func(t *testing.T) {
-		std.TestSkipHeights(int64(TIMESTAMP_90DAYS) / 2)
-		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
-		shouldEQ(t, reward, uint64(184437911))
+	t.Run("180day tier is over", func(t *testing.T) {
+		std.TestSkipHeights(int64(TIMESTAMP_180DAYS) / 2)
+		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+		shouldEQ(t, reward, uint64(645544122))
 		println()
 	})
 
-	t.Run("more block after 90 days", func(t *testing.T) {
-		std.TestSkipHeights(int64(TIMESTAMP_90DAYS) / 2)
-		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
+	t.Run("more block after 180 days", func(t *testing.T) {
+		std.TestSkipHeights(int64(TIMESTAMP_180DAYS) / 2)
+		reward := CollectRewardByDepositId(`gno.land/r/onbloc/obl:124:180:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:129`)
 		shouldEQ(t, reward, uint64(0))
 		println()
 	})
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier30_single_deposit_01_deposit_collect_gns_test.gnoA b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier30_single_deposit_01_deposit_collect_gns_test.gnoA
index 9fa3b38f8bd..9d7f3668e83 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier30_single_deposit_01_deposit_collect_gns_test.gnoA
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_tier30_single_deposit_01_deposit_collect_gns_test.gnoA
@@ -76,8 +76,8 @@ func TestCreateProject(t *testing.T) {
 		shouldEQ(t, tier30.tierAmount, uint64(100000000))
 		shouldNEQ(t, tier30.tierAmountPerBlockX96.ToString(), `0`)
 		shouldEQ(t, tier30.endTime, project.startTime+TIMESTAMP_30DAYS)
-		shouldEQ(t, tier30.depositAmount, uint64(0))
-		shouldEQ(t, tier30.participant, uint64(0))
+		shouldEQ(t, tier30.actualDepositAmount, uint64(0))
+		shouldEQ(t, tier30.actualParticipant, uint64(0))
 
 		shouldEQ(t, project.tier90Ratio, uint64(20))
 		tier90 := project.tier90
@@ -86,8 +86,8 @@ func TestCreateProject(t *testing.T) {
 		shouldEQ(t, tier90.tierAmount, uint64(200000000))
 		shouldNEQ(t, tier90.tierAmountPerBlockX96.ToString(), `0`)
 		shouldEQ(t, tier90.endTime, project.startTime+TIMESTAMP_90DAYS)
-		shouldEQ(t, tier90.depositAmount, uint64(0))
-		shouldEQ(t, tier90.participant, uint64(0))
+		shouldEQ(t, tier90.actualDepositAmount, uint64(0))
+		shouldEQ(t, tier90.actualParticipant, uint64(0))
 
 		shouldEQ(t, project.tier180Ratio, uint64(70))
 		tier180 := project.tier180
@@ -96,8 +96,8 @@ func TestCreateProject(t *testing.T) {
 		shouldEQ(t, tier180.tierAmount, uint64(700000000))
 		shouldNEQ(t, tier180.tierAmountPerBlockX96.ToString(), `0`)
 		shouldEQ(t, tier180.endTime, project.startTime+TIMESTAMP_180DAYS)
-		shouldEQ(t, tier180.depositAmount, uint64(0))
-		shouldEQ(t, tier180.participant, uint64(0))
+		shouldEQ(t, tier180.actualDepositAmount, uint64(0))
+		shouldEQ(t, tier180.actualParticipant, uint64(0))
 
 		shouldEQ(t, project.createdHeight, uint64(124))
 		shouldEQ(t, project.createdTime, uint64(1234567892))
@@ -135,8 +135,8 @@ func TestDepositGnsToTier30(t *testing.T) {
 		shouldEQ(t, tier30.tierAmount, uint64(100000000))
 		shouldNEQ(t, tier30.tierAmountPerBlockX96.ToString(), `0`)
 		shouldEQ(t, tier30.endTime, project.startTime+TIMESTAMP_30DAYS)
-		shouldEQ(t, tier30.depositAmount, uint64(0))
-		shouldEQ(t, tier30.participant, uint64(0))
+		shouldEQ(t, tier30.actualDepositAmount, uint64(0))
+		shouldEQ(t, tier30.actualParticipant, uint64(0))
 
 		shouldEQ(t, len(deposits), 0)
 		shouldEQ(t, len(depositsByProject), 0)
@@ -162,8 +162,8 @@ func TestDepositGnsToTier30(t *testing.T) {
 		shouldEQ(t, tier30.tierAmount, uint64(100000000))
 		shouldNEQ(t, tier30.tierAmountPerBlockX96.ToString(), `0`)
 		shouldEQ(t, tier30.endTime, project.startTime+TIMESTAMP_30DAYS)
-		shouldEQ(t, tier30.depositAmount, uint64(1000000))
-		shouldEQ(t, tier30.participant, uint64(1))
+		shouldEQ(t, tier30.actualDepositAmount, uint64(1000000))
+		shouldEQ(t, tier30.actualParticipant, uint64(1))
 
 		//
 		shouldEQ(t, len(deposits), 1)
@@ -247,6 +247,6 @@ func TestCollectDepositGns(t *testing.T) {
 	tier30 := project.tier30
 	shouldEQ(t, tier30.id, `gno.land/r/onbloc/obl:124:30`)
 	shouldEQ(t, tier30.tierAmount, uint64(100000000))
-	shouldEQ(t, tier30.depositAmount, uint64(0))
-	shouldEQ(t, tier30.participant, uint64(0))
+	shouldEQ(t, tier30.actualDepositAmount, uint64(0))
+	shouldEQ(t, tier30.actualParticipant, uint64(0))
 }
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_two_project_two_tier_two_deposit_collect_gns_test.gnoA b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_two_project_two_tier_two_deposit_collect_gns_test.gnoA
new file mode 100644
index 00000000000..d08916b7551
--- /dev/null
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_two_project_two_tier_two_deposit_collect_gns_test.gnoA
@@ -0,0 +1,179 @@
+package launchpad
+
+import (
+	"std"
+	"testing"
+	"time"
+
+	"gno.land/p/demo/testutils"
+
+	"gno.land/r/gnoswap/v2/consts"
+
+	"gno.land/r/gnoswap/v2/gns"
+	"gno.land/r/onbloc/bar"
+	"gno.land/r/onbloc/obl"
+)
+
+var (
+	projectAddr = testutils.TestAddress("projectAddr")
+	user01      = testutils.TestAddress("user01")
+
+	projectRealm = std.NewUserRealm(projectAddr)
+	user01Realm  = std.NewUserRealm(user01)
+)
+
+func init() {
+	println("test_init")
+}
+
+func TestCreateProjectObl(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+
+	obl.Approve(a2u(consts.LAUNCHPAD_ADDR), uint64(1_000_000_000))
+	std.TestSkipHeights(1)
+
+	projectId := CreateProject(
+		oblPath,
+		projectAddr,
+		uint64(1_000_000_000), // 1000000000
+		"",
+		"",
+		uint64(10),                   // 100000000
+		uint64(20),                   // 200000000
+		uint64(70),                   // 700000000
+		uint64(time.Now().Unix()+10), // 10s later
+	)
+	shouldEQ(t, projectId, `gno.land/r/onbloc/obl:124`)
+	std.TestSkipHeights(1)
+}
+
+func TestCreateProjectBar(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+
+	bar.Approve(a2u(consts.LAUNCHPAD_ADDR), uint64(1_000_000_000))
+	std.TestSkipHeights(1)
+
+	projectId := CreateProject(
+		barPath,
+		projectAddr,
+		uint64(1_000_000_000), // 1000000000
+		"",
+		"",
+		uint64(10),                   // 100000000
+		uint64(20),                   // 200000000
+		uint64(70),                   // 700000000
+		uint64(time.Now().Unix()+10), // 10s later
+	)
+	shouldEQ(t, projectId, `gno.land/r/onbloc/bar:126`)
+	std.TestSkipHeights(1)
+}
+
+func TestDepositGnsToOblTier30First(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+	gns.Transfer(a2u(user01), uint64(1_000_000)) // to deposit
+
+	std.TestSetRealm(user01Realm)
+	gns.Approve(a2u(consts.LAUNCHPAD_ADDR), uint64(1_000_000))
+
+	// skip some blocks to make project active
+	std.TestSkipHeights(4)
+
+	depositId := DepositGns("gno.land/r/onbloc/obl:124:30", uint64(1_000_000)) // 1000000
+	shouldEQ(t, depositId, `gno.land/r/onbloc/obl:124:30:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:131`)
+	std.TestSkipHeights(1)
+}
+
+func TestDepositGnsToOblTier30Second(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+	gns.Transfer(a2u(user01), uint64(1_000_000)) // to deposit
+
+	std.TestSetRealm(user01Realm)
+	gns.Approve(a2u(consts.LAUNCHPAD_ADDR), uint64(1_000_000))
+
+	depositId := DepositGns("gno.land/r/onbloc/obl:124:30", uint64(1_000_000)) // 1000000
+	shouldEQ(t, depositId, `gno.land/r/onbloc/obl:124:30:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:132`)
+	std.TestSkipHeights(1)
+}
+
+func TestDepositGnsToOblTier90First(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+	gns.Transfer(a2u(user01), uint64(1_000_000)) // to deposit
+
+	std.TestSetRealm(user01Realm)
+	gns.Approve(a2u(consts.LAUNCHPAD_ADDR), uint64(1_000_000))
+
+	depositId := DepositGns("gno.land/r/onbloc/obl:124:90", uint64(1_000_000)) // 1000000
+	shouldEQ(t, depositId, `gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:133`)
+	std.TestSkipHeights(1)
+}
+
+func TestDepositGnsToOblTier90Second(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+	gns.Transfer(a2u(user01), uint64(2_000_000)) // to deposit
+
+	std.TestSetRealm(user01Realm)
+	gns.Approve(a2u(consts.LAUNCHPAD_ADDR), uint64(2_000_000))
+
+	depositId := DepositGns("gno.land/r/onbloc/obl:124:90", uint64(2_000_000)) // 2000000
+	shouldEQ(t, depositId, `gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:134`)
+	std.TestSkipHeights(1)
+}
+
+func TestDepositGnsToBarTier30(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+	gns.Transfer(a2u(user01), uint64(1_000_000)) // to deposit
+
+	std.TestSetRealm(user01Realm)
+	gns.Approve(a2u(consts.LAUNCHPAD_ADDR), uint64(1_000_000))
+
+	depositId := DepositGns("gno.land/r/onbloc/bar:126:30", uint64(1_000_000)) // 1000000
+	shouldEQ(t, depositId, `gno.land/r/onbloc/bar:126:30:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:135`)
+	std.TestSkipHeights(1)
+}
+
+func TestCollectDepositGnsByProjectIdObl_Tier30(t *testing.T) {
+	std.TestSetRealm(user01Realm)
+
+	t.Run("claim before 30 tier ends", func(t *testing.T) {
+		claimed := CollectDepositGnsByProjectId("gno.land/r/onbloc/obl:124")
+		std.TestSkipHeights(1)
+		shouldEQ(t, claimed, uint64(0))
+	})
+
+	t.Run("claim after 30 tier ends from entire obl project's deposit", func(t *testing.T) {
+		std.TestSkipHeights(int64(TIMESTAMP_30DAYS) / 2)
+		claimed := CollectDepositGnsByProjectId("gno.land/r/onbloc/obl:124")
+		std.TestSkipHeights(1)
+		shouldEQ(t, claimed, uint64(2000000))
+	})
+}
+
+func TestCollectDepositGnsByProjectIdObl_Tier90(t *testing.T) {
+	std.TestSetRealm(user01Realm)
+
+	t.Run("claim before 90 tier ends", func(t *testing.T) {
+		claimed := CollectDepositGnsByProjectId("gno.land/r/onbloc/obl:124")
+		std.TestSkipHeights(1)
+		shouldEQ(t, claimed, uint64(0))
+	})
+
+	t.Run("claim after 90 tier ends, from certain deposit (first deposit)", func(t *testing.T) {
+		std.TestSkipHeights(int64(TIMESTAMP_90DAYS) / 2)
+		claimed := CollectDepositGnsByDepositId("gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:133")
+		std.TestSkipHeights(1)
+		shouldEQ(t, claimed, uint64(1000000))
+	})
+
+	t.Run("claim after 90 tier ends, from certain deposit (second deposit)", func(t *testing.T) {
+		std.TestSkipHeights(int64(TIMESTAMP_90DAYS) / 2)
+		claimed := CollectDepositGnsByDepositId("gno.land/r/onbloc/obl:124:90:g1w4ek2u3sx9047h6lta047h6lta047h6lh0ssfv:134")
+		std.TestSkipHeights(1)
+		shouldEQ(t, claimed, uint64(2000000))
+	})
+
+	t.Run("claim after 90 tier ends, from entire obl project's deposit", func(t *testing.T) {
+		claimed := CollectDepositGnsByProjectId("gno.land/r/onbloc/obl:124")
+		std.TestSkipHeights(1)
+		shouldEQ(t, claimed, uint64(0))
+	})
+}
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_deposit.gno b/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_deposit.gno
index 62eac030427..a7a32b09d18 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_deposit.gno
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_deposit.gno
@@ -77,8 +77,10 @@ func DepositGns(
 	gs.SetAmountByProjectWallet(project.recipient, amount, true) // true == add
 
 	// update tier
-	tier.depositAmount += amount
-	tier.participant += 1
+	tier.totalDepositAmount += amount
+	tier.actualDepositAmount += amount
+	tier.totalParticipant += 1
+	tier.actualParticipant += 1
 	project = setTier(project, tierStr, tier)
 
 	// update project
@@ -143,7 +145,7 @@ func DepositGns(
 //
 // returns collected gns amount
 func CollectDepositGns() uint64 {
-	calculateDepositReward() // uncomment this line if L#208 `CollectReward` is removed
+	calculateDepositReward()
 
 	caller := std.PrevRealm().Addr()
 	userDeposits := depositsByUser[caller]
@@ -179,8 +181,8 @@ func CollectDepositGns() uint64 {
 		gs.SetAmountByProjectWallet(project.recipient, deposit.amount, false) // subtract
 
 		// update tier
-		tier.depositAmount -= deposit.amount
-		tier.participant -= 1
+		tier.actualDepositAmount -= deposit.amount
+		tier.actualParticipant -= 1
 
 		// update project
 		project = setTier(project, deposit.tier, tier)
@@ -201,8 +203,90 @@ func CollectDepositGns() uint64 {
 		xgns.Burn(a2u(consts.LAUNCHPAD_ADDR), gnsToUser)
 		gns.Transfer(a2u(caller), gnsToUser)
 
-		// umcomment L#147 `calculateDepositReward()`
-		// CollectReward()
+		return gnsToUser // return accumulated gns amount being withdrawn
+	}
+
+	return 0
+}
+
+// CollectDepositGnsByProjectId collect deposited gns of certain project by caller
+// - gns will be transfered from the `launchpad` to caller
+// - launchpad's xgns will be burned
+//
+// returns collected gns amount
+func CollectDepositGnsByProjectId(projectId string) uint64 {
+	_, exist := projects[projectId]
+	if !exist {
+		println("NO PROJECT FOR THIS ID", projectId)
+		return 0
+	}
+
+	caller := std.PrevRealm().Addr()
+	if _, exist := depositsByUserByProject[caller]; !exist {
+		println("NO DEPOSIT FOR THIS USER", caller)
+		return 0
+	}
+
+	depositIds, exist := depositsByUserByProject[caller][projectId]
+	if !exist {
+		println("NO DEPOSIT FOR THIS PROJECT", projectId, " FOR UESR", caller)
+		return 0
+	}
+
+	calculateDepositReward()
+
+	gnsToUser := uint64(0)
+	for _, depositId := range depositIds {
+		deposit := deposits[depositId]
+
+		// check active
+		project, exist := projects[deposit.projectId]
+		if !exist {
+			panic(ufmt.Sprintf("SHOULD_NOT_HAPPEN__project not found: %s", deposit.projectId))
+		}
+
+		tier := getTier(project, deposit.tier)
+		if checkTierActive(project, tier) {
+			println("CollectDepositGns()_STILL ACTIVE TIER", deposit.tier)
+			continue
+		}
+
+		// collected
+		if deposit.depositCollectHeight != 0 {
+			continue
+		}
+
+		deposit.depositCollectHeight = uint64(std.GetHeight())
+		deposit.depositCollectTime = uint64(time.Now().Unix())
+		deposits[deposit.id] = deposit
+
+		gnsToUser += deposit.amount
+
+		// update gov_staker contract's variable to calculate proejct's recipient's reward
+		gs.SetAmountByProjectWallet(project.recipient, deposit.amount, false) // subtract
+
+		// update tier
+		tier.actualDepositAmount -= deposit.amount
+		tier.actualParticipant -= 1
+
+		// update project
+		project = setTier(project, deposit.tier, tier)
+		project.actualDepositAmount -= deposit.amount
+		project.actualParticipant -= 1
+		projects[deposit.projectId] = project
+
+		// emit event for each deposit
+		std.Emit(
+			"CollectDepositGns",
+			"m_prevRealm", prevRealm(),
+			"depositId", depositId,
+			"amount", ufmt.Sprintf("%d", deposit.amount),
+		)
+	}
+
+	if gnsToUser > 0 {
+		xgns.Burn(a2u(consts.LAUNCHPAD_ADDR), gnsToUser)
+		gns.Transfer(a2u(caller), gnsToUser)
 
 		return gnsToUser // return accumulated gns amount being withdrawn
 	}
@@ -210,6 +294,79 @@ func CollectDepositGns() uint64 {
 	return 0
 }
 
+// CollectDepositGnsByDepositId collect deposited gns of certain deposit
+// - gns will be transfered from the `launchpad` to caller
+// - launchpad's xgns will be burned
+//
+// returns collected gns amount
+func CollectDepositGnsByDepositId(depositId string) uint64 {
+	deposit, exist := deposits[depositId]
+	if !exist {
+		println("DEPOSIT NOT FOUND", depositId)
+		return 0
+	}
+
+	project, exist := projects[deposit.projectId]
+	if !exist {
+		println("NO PROJECT FOR THIS ID", deposit.projectId)
+		return 0
+	}
+
+	caller := std.PrevRealm().Addr()
+	if _, exist := depositsByUserByProject[caller]; !exist {
+		println("NO DEPOSIT FOR THIS USER", caller)
+		return 0
+	}
+
+	calculateDepositReward()
+
+	// check active
+	tier := getTier(project, deposit.tier)
+	if checkTierActive(project, tier) {
+		println("CollectDepositGns()_STILL ACTIVE TIER", deposit.tier)
+		return 0
+	}
+
+	// collected
+	if deposit.depositCollectHeight != 0 {
+		return 0
+	}
+
+	deposit.depositCollectHeight = uint64(std.GetHeight())
+	deposit.depositCollectTime = uint64(time.Now().Unix())
+	deposits[deposit.id] = deposit
+
+	// update gov_staker contract's variable to calculate proejct's recipient's reward
+	gs.SetAmountByProjectWallet(project.recipient, deposit.amount, false) // subtract
+
+	// update tier
+	tier.actualDepositAmount -= deposit.amount
+	tier.actualParticipant -= 1
+
+	// update project
+	project = setTier(project, deposit.tier, tier)
+	project.actualDepositAmount -= deposit.amount
+	project.actualParticipant -= 1
+	projects[deposit.projectId] = project
+
+	// emit event for each deposit
+	std.Emit(
+		"CollectDepositGns",
+		"m_prevRealm", prevRealm(),
+		"p_depositId", depositId,
+		"amount", ufmt.Sprintf("%d", deposit.amount),
+	)
+
+	if deposit.amount > 0 {
+		xgns.Burn(a2u(consts.LAUNCHPAD_ADDR), deposit.amount)
+		gns.Transfer(a2u(caller), deposit.amount)
+
+		return deposit.amount //
+	}
+
+	return 0
+}
+
 func getProjectIdFromTierId(tierId string) string {
 	// input: gno.land/r/gnoswap/gns:123:30
 	// output: gno.land/r/gnoswap/gns:123
@@ -241,7 +398,12 @@ func checkDepositConditions(project Project) {
 			continue
 		} else {
 			// check balance
-			balance := balanceOfByRegisterCall(condition.tokenPath, std.PrevRealm().Addr())
+			var balance uint64
+			if condition.tokenPath == consts.GOV_XGNS_PATH {
+				balance = xgns.BalanceOf(a2u(std.PrevRealm().Addr()))
+			} else {
+				balance = balanceOfByRegisterCall(condition.tokenPath, std.PrevRealm().Addr())
+			}
 			if balance < condition.minAmount {
 				panic(ufmt.Sprintf("insufficient balance(%d) for token(%s)", balance, condition.tokenPath))
 			}
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_init.gno b/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_init.gno
index 878b44b1bea..79e8898a6b7 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_init.gno
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_init.gno
@@ -63,6 +63,9 @@ func CreateProject(
 	if strings.Contains(conditionsToken, "*PAD*") {
 		tokensToCheck := strings.Split(conditionsToken, "*PAD*")
 		for _, token := range tokensToCheck {
+			if token == consts.GOV_XGNS_PATH {
+				continue
+			}
 			if _, exist := registered[token]; !exist {
 				panic(ufmt.Sprintf("condition token(%s) not registered", token))
 			}
@@ -186,8 +189,8 @@ func CreateProject(
 		"p_tier30Ratio", ufmt.Sprintf("%d", tier30Ratio),
 		"p_tier90Ratio", ufmt.Sprintf("%d", tier90Ratio),
 		"p_tier180Ratio", ufmt.Sprintf("%d", tier180Ratio),
-		"p_startHeight", ufmt.Sprintf("%d", startHeight),
 		"p_startTime", ufmt.Sprintf("%d", startTime),
+		"startHeight", ufmt.Sprintf("%d", startHeight),
 		"projectId", projectId,
 
 		"tier30Amount", ufmt.Sprintf("%d", tier30Amount),
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_reward.gno b/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_reward.gno
index 210d90ab885..edc692e115e 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_reward.gno
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/launchpad_reward.gno
@@ -17,7 +17,7 @@ func CollectProtocolFee() {
 	caller := std.PrevRealm().Addr()
 	gs.CollectRewardFromLaunchPad(caller)
 
-	// XXX: emit event
+	// event will be emitted in gov/staker CollectRewardFromLaunchPad()
 }
 
 var (
@@ -28,82 +28,6 @@ func init() {
 	lastCalculatedHeight = uint64(std.GetHeight())
 }
 
-// CollectReward collects reward from entire deposit by caller
-func CollectReward() {
-	calculateDepositReward()
-
-	caller := std.PrevRealm().Addr()
-	depositIds, exist := depositsByUser[caller]
-	if !exist {
-		println("NO DEPOSIT FOR THIS USER", caller)
-		return
-	}
-
-	// project token -> reward amount
-	toUser := make(map[string]uint64)
-
-	for _, depositId := range depositIds {
-		deposit := deposits[depositId]
-		if deposit.rewardAmount == 0 {
-			println("NO REWARD FOR THIS DEPOSIT", depositId)
-			continue
-		}
-
-		project := projects[deposit.projectId]
-		projectToken := project.tokenPath
-
-		if deposit.rewardAmount > 0 { // deposit has some reward
-			if deposit.rewardCollectTime != 0 { // this collect is not first collect
-				println("(N)th collect")
-				toUser[projectToken] += deposit.rewardAmount
-			} else {
-				// if fisrt collect, then check tier's collect wait duration
-				collectableAfter := uint64(0)
-				switch deposit.tier {
-				case "30":
-					collectableAfter = project.startHeight + project.tier30.collectWaitDuration
-				case "90":
-					collectableAfter = project.startHeight + project.tier90.collectWaitDuration
-				case "180":
-					collectableAfter = project.startHeight + project.tier180.collectWaitDuration
-				}
-
-				if uint64(std.GetHeight()) < collectableAfter {
-					println("NOT CLAIMABLE YET")
-					continue
-				}
-
-				println("token:", projectToken, "reward:", deposit.rewardAmount)
-				toUser[projectToken] += deposit.rewardAmount
-			}
-
-			std.Emit(
-				"CollectReward",
-				"m_prevRealm", prevRealm(),
-				"depositId", depositId,
-				"amount", ufmt.Sprintf("%d", deposit.rewardAmount),
-			)
-		}
-
-		// update project
-		project.totalCollectedAmount += deposit.rewardAmount
-		projects[deposit.projectId] = project
-
-		// update deposit
-		deposit.rewardAmount = 0
-		deposit.rewardCollectHeight = uint64(std.GetHeight())
-		deposit.rewardCollectTime = uint64(time.Now().Unix())
-		deposits[depositId] = deposit
-	}
-
-	// transfer reward to user
-	for tokenPath, amount := range toUser {
-		// println("tokenPath:", tokenPath)
-		// println("amount:", amount)
-		transferByRegisterCall(tokenPath, std.PrevRealm().Addr(), amount)
-	}
-}
-
 // CollectRewardByProjectId collects reward from entire deposit of certain project by caller
 //
 // returns collected reward amount
@@ -157,7 +81,7 @@ func CollectRewardByProjectId(projectId string) uint64 {
 				}
 
 				if uint64(std.GetHeight()) < collectableAfter {
-					println("NOT CLAIMABLE YET")
+					println("NOT CLAIMABLE YET", std.GetHeight(), "<", collectableAfter)
 					continue
 				}
 
@@ -185,107 +109,9 @@ func CollectRewardByProjectId(projectId string) uint64 {
 		deposits[depositId] = deposit
 	}
 
-	// transfer reward to user
-
-	transferByRegisterCall(project.tokenPath, std.PrevRealm().Addr(), toUser)
-
-	// XXX: emit event
-
-	return toUser
-}
-
-// CollectRewardByProjectTier collects reward from entire deposit of certain project tier by caller
-//
-// returns collected reward amount
-func CollectRewardByProjectTier(tierId string) uint64 {
-	projectId, tierStr := getProjectIdAndTierFromTierId(tierId)
-	project, exist := projects[projectId]
-	if !exist {
-		println("NO PROJECT FOR THIS ID", projectId)
-		return 0
-	}
-
-	caller := std.PrevRealm().Addr()
-	if _, exist := depositsByUserByProject[caller]; !exist {
-		println("NO DEPOSIT FOR THIS USER", caller)
-		return 0
-	}
-	depositIds, exist := depositsByUserByProject[caller][projectId]
-	if !exist {
-		println("NO DEPOSIT FOR THIS PROJECT", projectId)
-		return 0
-	}
-
-	calculateDepositReward()
-
-	toUser := uint64(0)
-	for _, depositId := range depositIds {
-		println("depositId:", depositId)
-		deposit := deposits[depositId]
-
-		// matching tier
-		if deposit.projectId == projectId && deposit.tier == tierStr {
-			if deposit.rewardAmount == 0 {
-				println("NO REWARD FOR THIS DEPOSIT", depositId)
-				continue
-			}
-
-			project := projects[deposit.projectId]
-			if project.id != projectId {
-				println("PROJECT ID MISMATCH", project.id, projectId)
-				continue
-			}
-
-			if deposit.rewardAmount > 0 {
-				if deposit.rewardCollectTime != 0 {
-					println("(N)th collect")
-					toUser += deposit.rewardAmount
-				} else {
-					collectableAfter := uint64(0)
-					switch deposit.tier {
-					case "30":
-						collectableAfter = project.startHeight + project.tier30.collectWaitDuration
-					case "90":
-						collectableAfter = project.startHeight + project.tier90.collectWaitDuration
-					case "180":
-						collectableAfter = project.startHeight + project.tier180.collectWaitDuration
-					}
-
-					if uint64(std.GetHeight()) < collectableAfter {
-						println("NOT CLAIMABLE YET")
-						continue
-					}
-
-					println("token:", project.tokenPath, "reward:", deposit.rewardAmount)
-					toUser += deposit.rewardAmount
-				}
-			}
-
-			std.Emit(
-				"CollectRewardByProjectTier",
-				"m_prevRealm", prevRealm(),
-				"p_tierId", tierId,
-				"depositId", depositId,
-				"amount", ufmt.Sprintf("%d", deposit.rewardAmount),
-			)
-		}
-
-		// update project
-		project.totalCollectedAmount += deposit.rewardAmount
-		projects[deposit.projectId] = project
-
-		// update deposit
-		deposit.rewardAmount = 0
-		deposit.rewardCollectHeight = uint64(std.GetHeight())
-		deposit.rewardCollectTime = uint64(time.Now().Unix())
-		deposits[depositId] = deposit
-	}
-
 	// transfer reward to user
 	transferByRegisterCall(project.tokenPath, std.PrevRealm().Addr(), toUser)
 
-	// XXX: emit event
-
 	return toUser
 }
 
@@ -293,7 +119,6 @@ func CollectRewardByProjectTier(tierId string) uint64 {
 //
 // returns collected reward amount
 func CollectRewardByDepositId(depositId string) uint64 {
-	println("CollectRewardByDepositId", depositId)
 	deposit, exist := deposits[depositId]
 	if !exist {
 		panic("deposit not found")
@@ -332,7 +157,7 @@ func CollectRewardByDepositId(depositId string) uint64 {
 			}
 
 			if uint64(std.GetHeight()) < collectableAfter {
-				println("NOT CLAIMABLE YET")
+				println("NOT CLAIMABLE YET", std.GetHeight(), "<", collectableAfter)
 				return 0
 			}
 
@@ -433,9 +258,9 @@ func calculateDepositReward() {
 
 		// calculate deposit ratio
 		// loop with each tier (30 90 180)
-		tier30Deposit := project.tier30.depositAmount
-		tier90Deposit := project.tier90.depositAmount
-		tier180Deposit := project.tier180.depositAmount
+		tier30Deposit := project.tier30.actualDepositAmount
+		tier90Deposit := project.tier90.actualDepositAmount
+		tier180Deposit := project.tier180.actualDepositAmount
 		// println("tier30.id", project.tier30.id)
 		// println("tier30Deposit", tier30Deposit)
 		// println("tier90Deposit", tier90Deposit)
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/type.gno b/examples/gno.land/r/gnoswap/v2/launchpad/type.gno
index 2567e34defa..8e749322e69 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/type.gno
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/type.gno
@@ -50,11 +50,11 @@ type Tier struct {
 	endHeight uint64
 	endTime   uint64
 
-	// actual data
-	// unlikely projects' totalDepositAmount or totalParticipant
-	// below data will be decreased
-	depositAmount uint64
-	participant   uint64
+	totalDepositAmount  uint64
+	actualDepositAmount uint64
+
+	totalParticipant  uint64
+	actualParticipant uint64
 }
 
 type Condition struct {

From c7940047da7d14d1ed890410aee790ad1b9fc8ff Mon Sep 17 00:00:00 2001
From: 0xTopaz <myunghwan@onbloc.xyz>
Date: Tue, 15 Oct 2024 21:25:48 +0900
Subject: [PATCH 2/2] test : add createProject Test

- when project have a condition that account has balance of xgns, DepositGns function should check balance of xgns
---
 .../__TEST_0_INIT_TOKEN_REGISTER_test.gno     |  23 +++-
 .../__TEST_launchpad_create_project_test.gno  | 103 ++++++++++++++++++
 2 files changed, 123 insertions(+), 3 deletions(-)
 create mode 100644 examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_create_project_test.gno

diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_0_INIT_TOKEN_REGISTER_test.gno b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_0_INIT_TOKEN_REGISTER_test.gno
index 4fb135a1925..051a2915701 100644
--- a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_0_INIT_TOKEN_REGISTER_test.gno
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_0_INIT_TOKEN_REGISTER_test.gno
@@ -15,6 +15,8 @@ import (
 
 	"gno.land/r/onbloc/obl"
 
+	"gno.land/r/onbloc/usdc"
+
 	"gno.land/r/gnoswap/v2/gns"
 
 	"gno.land/r/gnoswap/v2/consts"
@@ -120,19 +122,31 @@ type GNSToken struct{}
 func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) {
 	return gns.Transfer
 }
-
 func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) {
 	return gns.TransferFrom
 }
-
 func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 {
 	return gns.BalanceOf
 }
-
 func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) {
 	return gns.Approve
 }
 
+type USDCToken struct{}
+
+func (USDCToken) Transfer() func(to pusers.AddressOrName, amount uint64) {
+	return usdc.Transfer
+}
+func (USDCToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) {
+	return usdc.TransferFrom
+}
+func (USDCToken) BalanceOf() func(owner pusers.AddressOrName) uint64 {
+	return usdc.BalanceOf
+}
+func (USDCToken) Approve() func(spender pusers.AddressOrName, amount uint64) {
+	return usdc.Approve
+}
+
 func init() {
 	std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER))
 
@@ -144,6 +158,7 @@ func init() {
 	pf.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{})
 	pf.RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{})
 	pf.RegisterGRC20Interface("gno.land/r/gnoswap/v2/gns", GNSToken{})
+	pf.RegisterGRC20Interface("gno.land/r/onbloc/usdc", USDCToken{})
 
 	// gov_staker
 	gs.RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{})
@@ -153,6 +168,7 @@ func init() {
 	gs.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{})
 	gs.RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{})
 	gs.RegisterGRC20Interface("gno.land/r/gnoswap/v2/gns", GNSToken{})
+	gs.RegisterGRC20Interface("gno.land/r/onbloc/usdc", USDCToken{})
 
 	RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{})
 	RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{})
@@ -161,4 +177,5 @@ func init() {
 	RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{})
 	RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{})
 	RegisterGRC20Interface("gno.land/r/gnoswap/v2/gns", GNSToken{})
+	RegisterGRC20Interface("gno.land/r/onbloc/usdc", USDCToken{})
 }
diff --git a/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_create_project_test.gno b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_create_project_test.gno
new file mode 100644
index 00000000000..170d4cb082b
--- /dev/null
+++ b/examples/gno.land/r/gnoswap/v2/launchpad/__TEST_launchpad_create_project_test.gno
@@ -0,0 +1,103 @@
+package launchpad
+
+import (
+	"std"
+	"testing"
+	"time"
+
+	"gno.land/p/demo/testutils"
+
+	"gno.land/r/gnoswap/v2/consts"
+	"gno.land/r/gnoswap/v2/gns"
+	gov_staker "gno.land/r/gnoswap/v2/gov/staker"
+	xgns "gno.land/r/gnoswap/v2/gov/xgns"
+	"gno.land/r/onbloc/obl"
+	"gno.land/r/onbloc/usdc"
+)
+
+var (
+	projectAddr = testutils.TestAddress("projectAddr")
+	user01      = testutils.TestAddress("user01")
+
+	projectRealm = std.NewUserRealm(projectAddr)
+	user01Realm  = std.NewUserRealm(user01)
+)
+
+func init() {
+	println("test_init")
+}
+
+func TestCreateProject(t *testing.T) {
+	var allocationTokenAmount uint64 = 1_000_000_000_000 // 1000000000000
+	std.TestSetRealm(gsaRealm)
+
+	obl.Approve(a2u(consts.LAUNCHPAD_ADDR), allocationTokenAmount)
+	std.TestSkipHeights(1)
+
+	projectId := CreateProject(
+		oblPath,
+		projectAddr,
+		allocationTokenAmount,
+		"gno.land/r/gnoswap/v2/gov/xgns*PAD*gno.land/r/onbloc/usdc",
+		"100000000*PAD*200000000",
+		uint64(50),                   // 50%
+		uint64(30),                   // 30%
+		uint64(20),                   // 20%
+		uint64(time.Now().Unix()+10), // 10s later
+	)
+	shouldEQ(t, projectId, `gno.land/r/onbloc/obl:124`)
+	std.TestSkipHeights(1)
+}
+
+func TestMockProtocolFee(t *testing.T) {
+	std.TestSetRealm(gsaRealm)
+	usdc.Transfer(a2u(consts.PROTOCOL_FEE_ADDR), 1_000_000_000)
+	gns.Transfer(a2u(consts.PROTOCOL_FEE_ADDR), 2_500_000_000)
+
+	shouldEQ(t, usdc.BalanceOf(a2u(consts.PROTOCOL_FEE_ADDR)), uint64(1_000_000_000))
+	shouldEQ(t, usdc.BalanceOf(a2u(consts.DEV_OPS)), uint64(0))
+	shouldEQ(t, usdc.BalanceOf(a2u(consts.GOV_STAKER_ADDR)), uint64(0))
+
+	shouldEQ(t, gns.BalanceOf(a2u(consts.PROTOCOL_FEE_ADDR)), uint64(2_500_000_000))
+	shouldEQ(t, gns.BalanceOf(a2u(consts.DEV_OPS)), uint64(0))
+	shouldEQ(t, gns.BalanceOf(a2u(consts.GOV_STAKER_ADDR)), uint64(0))
+}
+
+func TestDepositGnsToTier30Failure(t *testing.T) {
+	var usdcConditionAmount uint64 = 200_000_000
+	var oblDepositAmount uint64 = 1_000_000
+	std.TestSetRealm(gsaRealm)
+	usdc.Transfer(a2u(user01), usdcConditionAmount)
+	std.TestSetRealm(user01Realm)
+	gns.Approve(a2u(consts.LAUNCHPAD_ADDR), oblDepositAmount)
+
+	std.TestSkipHeights(4)
+	shouldPanicWithMsg(
+		t,
+		func() {
+			DepositGns("gno.land/r/onbloc/obl:124:30", oblDepositAmount)
+		},
+		`insufficient balance(0) for token(gno.land/r/gnoswap/v2/gov/xgns)`,
+	)
+	std.TestSkipHeights(1)
+}
+
+func TestDepositGnsToTier30WithCondition(t *testing.T) {
+	var xGNSConditionAmount uint64 = 100_000_000
+	var gnsDepositAmount uint64 = 1_000_000
+	std.TestSetRealm(gsaRealm)
+	gns.Transfer(a2u(user01), xGNSConditionAmount+gnsDepositAmount)
+	shouldEQ(t, gns.BalanceOf(a2u(user01)), xGNSConditionAmount+gnsDepositAmount) // admin has initial gns
+	shouldEQ(t, xgns.BalanceOf(a2u(user01)), uint64(0))
+
+	std.TestSetRealm(user01Realm)
+	gns.Approve(a2u(consts.GOV_STAKER_ADDR), xGNSConditionAmount)
+	gov_staker.Delegate(user01, xGNSConditionAmount)
+	shouldEQ(t, xgns.BalanceOf(a2u(user01)), xGNSConditionAmount)
+	std.TestSkipHeights(4)
+
+	var beforeDepositGns uint64 = gns.BalanceOf(a2u(consts.LAUNCHPAD_ADDR))
+	DepositGns("gno.land/r/onbloc/obl:124:30", gnsDepositAmount)
+	shouldEQ(t, gns.BalanceOf(a2u(consts.LAUNCHPAD_ADDR)), beforeDepositGns+gnsDepositAmount)
+	std.TestSkipHeights(1)
+}