00:00
好,上节课我们已经说到了,就是我们已经实现了基本的这个购买token的这个方法,那接下来呢,我们就得去实现别的东西了,对吧?大家肯定想到,呃,我们首先除了购买这个token之外,接下来还有一个很重要的方法应该就是呃,拿到的token我们要给人投票,对吧?所以大家应该能够想到。我们这里接下来一个最重要的方法应该就是vote for candidate for candidate,这个大家很熟悉了,之前我们肯定是要把这个要选的,这个候选人要传进来的,对吧。但是在这里我们又跟之前不一样,之前我们只传候选人要选谁就完了,呃,传一个Alice Bob什么的就完了,那现在我们还得给一个,你要给他投多少票对吧?所以还得vote vote tokens,我们还得有一个这样的参数,当然这个投票也是公开的,对吧?Public,好,那接下来我们就来看一下这个投票的时候应该是一个什么样的流程呢?大家先想一下,首先应该大家能够想到我们投票的话是需要。
01:33
呃,大家首先想一下,我们投票需要什么样的限制条件?票数额,嗯,首先应该他那个手上有票对吧,这个有票的话,这个肯定是应该有的,但大家首先注意啊,就是我们这里首先得判断就是他投的这个人在我们的是我们的候选人对吧?所以这里面我们肯定要require哦,这里这个自动补全,突然这么。
02:06
这么智能有点不适应啊,我们直接去require一个,大家应该能够想到就是我们,呃,投票人应该在我们的例子里边,那这个东西我们怎么样去处理呢?我们可以去定义一个变量,去就是指定成我们。候选人在候选人数组里边的那个下标,那个索引,我们把这个拿出来对吧,所以我们这里可以定义一个方法,比如说叫index of candidate。定义一个这样的方法,我们只要把candidate这个name传进去。然后它就可以给我们返回一个对应的在我们数组里边的那个下边,假如说没有的话,我们给他返回一个负一,那这个就相当于我们起到了查询它,而且我们直接拿到了这个下标是吧?之后我们用起来也会方便一点,因为我们随时要去用这个can list这个里面的东西,所以我们拿到下标的话,就可以直接往candidate list里边去去找这个东西了。好,那接下来我们require肯定就要require index。
03:28
应该是不能是负一对吧,不等于负一,在大家这里注意一下,我们把负一做一个那个的转换吧。那下面呢,就想到我们肯定要实现一个function,叫做index of candidate,那么它的输入应该是一个BIT32CANDIDATE。呃,这个就不需要是public了,对吧?呃,而且这个应该是一个real类型,对不对,他不需要去更改东西对吧?那接下来大家会想一下它的这个函数题,我们应该去怎么样去实现呢。
04:15
那其实很简单,就应该是一个就应该是一个循环了,对不对,那我们直接定义一个for,那这个是不是还应该有一个返回值啊。对。需要去返回一个unit类型的值,对吧,它的索引,所以接下来我们定义一个for循环那一个I吧。等于零。小鱼。Can candidate list.plans I,加加,这都是最基本的这个for循环的写法,所以这个大家就过一遍就行。那如果说我们这个candidate,假如说它跟我们当前查到的这个便历到的candidate list里边的第二个元素相等的话,那么我们就return这个小,对吧?
05:25
是不是这样,然后else啊,那那其实不是else,那其实就是你整个这么查,如果一直查到最后结束都没有直接返回小标的话,那就说明没有,我们就返回一个负一对吧?啊大尔法负一大也限定一下它的这个类型啊负一好,那么我们上边有了这个方法之后,在这里我们就直接可以,我这是不是少了一个括号。就直接可以根据index是否等于负一来判断我们的candidate是不是在这个candidate list里面,对吧?好,这是我们一开始的一个很简单的一个条件判断,好,那么接下来大家就会看到。
06:13
我们还应该去做一个什么事情呢?首先要做这个投票,那么大家就会想到投票的时候我们要去更改的应该是这个,呃,Votes received这个方法对吧?另外还应该更改tokens load for candidates这个数组对不对?大家注意一下我们这个votes received这个里边。如果我们已经定义了这个三二的话,那么在mapping里边它初始肯定就是零了,这个我们其实不需要去做太多的初始化,但是大家注意,我们这里的这个tokens vote for candidates,这是一个变长数组,对吧?所以一开始其实我们是没有给值的。
07:04
没有初始化的,所以这样的东西其实是需要大家就是要要注意一下,这个东西是比较危险的,大家知道这里我们是定义在了这个状态变量里面的。这里这个water,这个mapping对吧,就放在这个状态变量里面,但是我们一开始如果要是刚刚他已经买了这个token之后,大家会发现我们只给address和token number赋了值。但是没有给这个这个数组对吧,Candidate去赋值,所以这个时候。在这个数组里面存着的是什么呢?这个这个时候存在的是什么呢?大家能想到吗?啊,我们当时其实跟大家说过啊,就是在这个storage这是状态变量,所以它是一个storage存储类型,对吧,Storage类型的变长数组,它本身存储的其实是它的长度值对吧,所以这个时候它相当于只存了只存了一个长度值。
08:19
而具体它的那个里边所存存着的内容呢,是要哈希出去到别的地方,根据它的这个索引去去哈希出去去找的,对吧?那像现在的这个状况,如果里面我们根本没有初始化,那其实它只有一个零代表它的长度。所以在这个时候我们再去给它去更改它里边的值之前,其实应该先给它去做一个对应的初始化,因为我们前面拿到的这个index,这是这是表示我们这是第几个candidate,大家能想到我们等一下就要给它对应的那个位置去插进candidate,对吧?那如果要是说没有初始化的话。
09:07
对应的那个位置我们插进去了,那别的位置是不是根本什么东西还没有啊,所以这其实就相当于是一个一个越界访问的一个状态了,对吧?啊,尽管这个变长数组呃,不存在这个这个越界访问的问题,但是大家会发现如果这样不去做初始化的话,可能会有问题,所以我们默认上来之后先把它等一下,我们既然是要访问第二个这个这个角标的。对应的那个值要给它改掉,那么我们先把它所有的都初始化一下,给一个零,那所以这个初始化的过程是什么呢?那首先我们应该判断一下它是不是已经有值了,对吧,因为它有可能是。已经初始化过,然后现在,呃,之前给别人投过一票,现在又给又给另外一个人投,有可能是这种情况,那这种情况我们不能再把他之前投过的票给清零了,对吧?所以我们先判断一下if,那我们判断什么呢?那就判断它的。
10:09
整个这个votes for candidate这个数组的长度吧。那这个东西怎么拿呢?呃,大家就会发现这个它是一个water里面的东对象,一个一个元素对吧,那water在哪里呢?Water我们得到这个mapping里面来拿对吧?所以我们又是vote vote infer,它里边根据message点三这个地址就能够拿这个就相当于是我们的water对象对不对。现在这就是water对象,好,那么对象它下边的,刚才我们说这个叫什么token votes for candidates这个数组,对吧,这个数组的lengths我们去判断一下。如果说它等于零的话,那我们去给它赋一个初值,是这样对不对,一旦负了初值之后,它是不是length就不是零了,对,所以说那下面这个负出值肯定又是一个for循环了,对吧?这个就UI等于零。
11:21
I小于,既然是负初值嘛,又是跟candidate相关对吧?有几个candidate,负几个初值,Candidate list length I加加,好,那么接下来大家应该就能够想到,我们就得把对应的那个就是这个loss for candidate里,那这个数组里边每一个对应的值都给负一个零对吧?负一个数值就完了,好,那么大家注意啊,还得跟上面这么一整串来写,大家如果要嫌麻烦的话,可以就是另外定义一个变量把它存下来,对吧?我这里就是直接这么写了啊,Tokens vote for candidates,然后诶,大家注意,这里我们其实是对于变长的。
12:15
数组来讲,有一个固定的方法可以给它在后面去追加元素,对吧?因为这里我们还没有定义它,所以直接用它的这个角标去访问其实是不可以的,所以我们这里应该去怎么去去给它追加元素呢?大家应该还记得有一个push方法对吧?变长数组有一个push方法,那我们直接push一个零,那就相当于给他付了一个零值。这就是我们这个呃付出值的这样一个过程,来考虑清楚我们整个这个呃产品在构建过程当中的一些细节,因为在这个情况下,如果我们这个初值不给的话,可能之后就会有问题,对吧?好,那现在已经有了储值了,那我们就应该可以把它要投的这些值给到这个人了,对吧?好,那么肯定大家会想到我们会去require。
13:12
Require一个什么呢?呃,啊,这里大家还得注意,就是说我们是不是应该得看他要投的这些票自己是不是有啊。我们先得判断一下,他手里边是不是还有这么多token对吧?诶那手里他手里边还有多少token,我们到哪去知道呢?这个我们前面定义的,好像没有给他定义一个token balance对吧。啊,这个我们好像失误了一下,没有定义那个,那我们现在是不是拿不到了呢。呃,但其实也不是对吧,因为大家发现下边我们是他已经投出去的token是不是都在这个数组里面记录着呀,所以那我们就是如果大家想冗余的话,我们可以再定一个U的token balance,对吧,每个人再定义一下它的余余额,Token余额,如果我们泛懒不想去做这个冗余,那我们在代码里面就得去复杂一点,每次都算一下它剩下多少对吧,所以呢,我们。
14:13
我们教案里面是没有没有定义这个balance的,所以我们就按教案上说怎么算呢?就先重新定义一个变量,对吧,比方说这个变量叫呃,我们就叫balance吧,应该叫什么balance。看一下教案上给的什么名字啊?偷偷好,这里还有偷号,我们等一下再实现啊。好,教案上给的这个名字叫available tokens好,这个还是好好认识一点是吧,就是说现在可用的可用的token,好,我们就按这个吧,诶,这为什么会同时有两个输入服,很神奇,Available。
15:03
好,那么我们定义一个available POS,它就应该是用我们,呃,当前vote infer又得把上面这个抄一起啊,点三。它里边的token number总数对吧,然后减掉每个人的对不对,Token number。哦,捡每个人的这个还有点麻烦,所以我们还是提取一个方法出来吧,比方说我们叫呃,Total use的token,然后我们定义一个啊,这个时候我们就把它的那个数组传进去,然后我们算个总数,对吧,就相当于做一个加法,把后面这个乘进去三的。点,呃,这个应该叫two votes for candidates对吧?好,我们把这个传进去,然后去做一个加法,那我们已经有了这个available tokens之后,就需要去require它。
16:14
这个available啊,Tokens必须要大于等于这里想要vote的tokens对吧?啊,这是我们这个最基本的一个要求,那接下来大家应该能够想到,我们是不是就应该把它那个呃票数就可以更改了,真正给他可以做这个票数加上去投票啊好,那么。首先他投给的那个人是不是应该票应该加上啊,我们看一下投的那个应该在哪里,记着叫votes received对吧?好,那么我们要的这个就应该是叫votes received,好难写啊。
17:05
Loans received。里边大家应该想到它是不是得找到我们对应的那个人啊,它定义的是一个三二到U啊,那所以我们直接这个船。传投给谁就可以了,对吧?这里是can已经定义了,所以我们直接把这个转进来,那么它的这个值对应的是一个u in,是不是就应该加上我们现在投的票对不对?因为之前有可能有别人给他投过了,所以这个数我们不能直接改,而是要加上我们现在要投的这个vote vote to,好,那同样这里加了,那就应该有一个地方是不是?呃,啊,这里我们不用减,因为我们没有那个balance,对吧?所以我们减的这个操作。
18:01
是不是直接把这个数记到他已经投出去的这一个地方就可以了,要减的时候我们用那个函数再算对吧?所以这就相当于是一个减,所以我们还要记一个叫做vote iner。他的messenger.sender下边的token votes for candidates,对吧?这个数组里边的谁呢?前面我们刚好存的这个index,对不对?这里就派上用用场了,我们要要改的就是这个数。Index。一开始就定义了对吧。对应的那个位置就是他投给第几个候选人,那个index就只代候选人的位置,这是唯一定义的,所以当然这里应该是加等于对吧,有可能他之前就投过,现在又想投追投几票呢?所以说我们这里也是加等于,加等于vote vote tokens,对吧?好,所以大家看到这里,我们就把整个的这个投票的环节就实现了,所以大家可以看到,我们一旦涉及到这个余额啊,这个数量变化的时候,那肯定就是一个地方加,就得考虑另外一个地方减。
19:22
我们这个地方没有那个记录余额,所以说我们这里不减,相反是使用这个加的方式,把它之前记录了,他这个投给谁,把这个地方加上去,所以我们就可以把它这个相当于余额就减了,对吧?啊,那当然了,大家就会发现我们之前还有一个方式没实现,也就是说算他的余额。呃,算它的那个花费总数的这个没没实现对吧,Total use的这个我们应该,呃,传入的是一个U。
20:00
UT的一个数组。我们把它叫做。呃,就叫这个。之前我们这个是叫什么for candidate对吧,好,我们就。Vote for candidate,好,传入这个数组,然后大家会想到,我们这里应该还得去。返回一个值对不对,Returns一个U,因为我们要返回它当前的总数嘛,对吧。呃,那大家可以想到这个是不是我们也根本不需要任何的改变呢?用一个view。就可以了,对吧。好,那接下来大家就会想到,我们在里边肯定就是定义一个总数了,对吧?Total tokens一开始定义等于零,然后给一个for循环,I等于零,I小于。
21:15
Votes,呃,我们注意一下,这里不要跟我们的状态变量重名了,是不是有这个votes for candidate了,如果要已经有重名变量的话,大家尽量不要跟这个状态变量弄成重名的,这里好像已经有这token for candidate对吧?我们这里把这个尽量还是换一个名字吧,好,我们这里没有啊,我们这里叫votes candidate,所以就没关系,所以我们还是用这个变量名称就可以了,Moves for candidate.length对吧?好,接下来我们爱加加那里边的内容就非常简单了,对不对,就是totals加等于我们的votes for candidate,它的第二个元素。
22:15
加起来就完了对吧?好,所以大家可以看到就是这我们就实现了这个投票的这个环节,诶这里有错误。好,这里其实是提示我们尽量可能好,这里我是address运行的vote address对吧,所以这里大家注意啊,大家没看到这个是不是。Vote address对吧,我们拿的是这个元素,不是拿的address地址类型对吧?对,所以大家看这个装了这个相关的插件之后,它这个就比较好,还可以把这样的错误能提示出来。这里又错了吗?
23:00
Ton。Use tokens。啊,方式写错了对吧,那这种错误就是大家就会发现这个错的很奇怪,这里好像还有问题啊。Total used tokens。我们看一下这里哪里错了,来帮我一起检查一下,诶没错了是吧,好吧,他这个反应有点慢是吧。呃哦,我们这里应该叫vote inver是吧?对,Vote infer就差一个字母,所以说它能够直接给我们检查出来,这个还是比较好,Vote in粉对吧。他反应不要不要再慢了啊,最好快一点。诶,这里怎么还。好,果然是反应比较慢,好所以大家看到现在这个就没有什么问题了,对吧,没有这个编译错误了,好那接下来诶,大家可以看到,其实我们在这个过程当中啊,大家注意这里少了一个什么东西啊。
24:14
我们加了半天,最后是不是没返回啊,对吧,大家没看到这个,我们是不是得把最后加好的这个to token给返回的。对吧,所以这才是我们这个真正的返回的类型,对吧?好,那么其实大家这里可以看到,我们在这个过程当中,其实只是传进来一个数组,然后把它加完了返回一个总数,其实这个连访问状态变量都没有,对吧?所以我们甚至可以不用view,我们可以把它改成Q,大家还记得吗?纯函数,什么叫纯函数?就是输入是同样的,然后输出就一定是同样的纯计算对吧?给一个输入就是同样的输出,所以这就叫纯函数,既不改我们的状态变量,也不读,所以可以把它直接定义成纯函数啊好,大家可以看到这我们基本上这个最主要的两个方法,一个是去购买代币,另外一个就是拿着代币去投票,这两个方法我们都已经实现了。
25:22
呃,当然,那接下来大家可能会想到还会有一个,刚才应该我们都看到了啊,有一个叫做我们还得查对吧,除了这个投票的话,我们还得查这个,查的这个函数叫什么名字。好,Total bos这个跟我们之前的那个名字还是一样的,对吧?好,所以我们这里还是定义一个查询的一个。方法叫total votes for,那同样这个要传的就应该是一个三二了,对吧,把这个candidate name传进来。呃,然后这个我们就应该要有一个返回值,返回一个U对不对,那这个其实也应该是view对吧,不去更改我们的状态,只是查询好,那么这里肯定大家就会想到直接可以return了,对吧?那我们去vote vote infer对吧,把me不是啊,我们不能去vote infer。
26:30
Total votes for是在这个votes received里面去查,去查对不对,直接这里是不是就能用这个BEST32查出来了,所以我们这个数据变这个数据类型定义的时候,就是方便这个去查询的,所以这个叫叫什么来着?Vote vote。那从里边我们直接拿出这个candidate来,直接返回就可以了,对吧?好,这是我们去查询当前有多少票这样一个一个方法,好那么接下来大家我们再想想,就是还需要查什么东西呢?我们这么一个一个应用,你再去看一看啊,除了这个totals for。
27:27
然后还应该去下面是投票对吧。这是我们刚才的两个方法。哦,大家会想到我们是不是在页面上有可能还得显示我们当前已经卖出去多少token,对吧?给大家显示一下诶,当前我们总共是多少token,然后已经卖出去多少token,那其实这个卖出去多少token也就是一减,我们因为我们有余额嘛,所以卖出去的直接就一减就可以。另外还有大家可能会想到我们是不是要去查看一下voter的这个信息呢?
28:04
它的这个呃,就是详细详情,详细信息是不是也要用一个函数去查询呢?那我们这个把它也去定义一个函数,到时候我们在页面上可以去显示啊,另外大家就会看到这个transfer肯定是可以给的,对吧?然后还有一个好,这是查询所有的candidate。那这里其实我们candidate list本来就是一个public类型,但是我们一般情况不要直接去用这个,就是本身这个状态变量,我们其实是可以把它去就是定义成我们的私有私有状态变量的,我们还是单独定义这个方法去做查询,所以这里面是给出了这么几个函数啊,好,那么这就很简单了,我们挨个来实现一下吧。首先是一个function,叫做呃,Token so,对吧,So token还是叫token so方啊,没关系,那这个肯定就是一个。
29:06
Public类型,而且是一个view对吧,直接查出来就好,所以大家可以看到它是需要有一个返回值的。既然是total so,所以我们直接return。看一下我们定义的状态变量,一个叫total token,一个叫token balance,对吧?所以剩下多少呢?当然就是token。减去token balance,好,这就是我们剩下的这个余额,呃,这就是我们已经卖掉的所有的token总数,对吧?这个我们就没有必要去查询,呃,当前所有人账户里面剩多少token,这个而且是查不到的,对吧?大家知道便利所有人的这个地址,便利我们的一个mapping里边的所有地址,所有的K,这个是做不到的啊,所以说我们一定是在状态变量里边,诶这个写错了是吧?
30:08
来看这个错误提示,提示的很到位。好,那接下来刚才我们还说到,除了查询通审的信息之外,还要查每个投票人的详情,对吧,所以我们定义一个。呃,Vote details这样的一个信息,那同样要查的话,我们肯定得传进你的地址,对吧,我们还是用地址来去指定一个voter的,叫user啊,叫vote。那每一个vote我们应该能想到这也是查询,所以说public view对吧。Public view,然后我们会去返回它的信息,那么我们看一下一个vote比较有效的信息是什么地址,肯定就不用返回了,这是需要传进去的信息,对吧?那大家可以看到一个是它的token numbers,一个是token vote for pen,对吧?所以我们把这两个信息返回,所以我们用这种返回多个参数的方式。
31:18
返回一个U,返回一个U数组对吧?好,接下来那我们要返回什么东西呢?我们直接查询返回当前用户的对应的这两个信息,那我们把它括起来吧,然后前面的是我们要返回的那个token numbers对吧?所以就是大家还记得我们那个vote吗?Vote对吧,这个确实定义定义成vote不太好啊,这个好像这个vote是我们定义的那个数据数据类型一样,我们还是定义一个vote吧A。
32:09
好,Water water infer里边选,选出对应的water address,拿到的这个就是整个的vote那个对象对吧,那个结构体,然后我们给他。点拿出来它的叫做token number。然后返回就可以了,对吧,同样后边这个也是一样的啊water in water address。后边后边我们去拿出来的是tokens。For for candidate对吧,这就是我们这个数组,把这两个拿出来,然后返回就可以了,诶上面又有错又拼错了是吧?Public。
33:06
好,接下来大家还记得我们还有个什么方法来着,还有一个transfer对吧,还有一个什么来着。嗯,看一眼好,还有一个all,这个就简单了,对吧。其实它就是把我们的candidate例子的返回,所以这个没有任何的难度啊,Public returns,那这个肯定就是一个三二的一个数组了,对吧。好,那么我们这里去return,呃,我们的candidate list,直接把这个返回就完事了,诶,又写错了。敲快了就容易写错,所以有一个好的IDE随时能去提醒,这个还是比较重要的,对吧?好,呃,那接下来我们把这个transfer也简单做一个实现吧,这个transfer这个内容大家可以想到,我们肯定就是要给谁去转币,对吧?呃,那这里我们就是定义一个address。
34:18
那我们的目标地址,比方说我定义一个图吧,呃,那这个我们可以直接就定义成public,但是这个肯定会有问题对吧?定义成public的话,那相当于谁都可以去转了,那这里是有一个安全风险的,所以这里给大家留作作业来,自己去把我们这个穿字本,这个函数就是安全性。考虑定义好,谁才有资格去从这个,因为大家看到这个穿刺是要干什么的。这个其实不是说一个人给另外一个人发币,对吧,我们的token的用途不是给别人转账的,就是投票用的,所以说这里不是说要去给别人拿我的token转给别人,而是说要从我们的账户里面提出币来,对不对?
35:11
所以这里的token,这里的transfer不是transfer token其实是transfer,我们合约里面打进去的以太,你要不给这个方法的话,打进去的以态就永远提不出来了,所以这个就是必须要实现的一个,当然我们这里面实现的这个安全性可能不太好,所以说大家之后把我们这个实现就可以了,所以大家还记得吗?我们怎么样去用合约给别人转币呢?就是这个合约地址,呃,不是,就是要给谁转币就拿他的地址,然后点transfer对不对,就是我们当前合约就会发出去,对吧,当前当时我们的水龙头合约就是这么写的,所以说大家可以忘记的话,可以再去。去把这个回顾一下,那当然这里我们可以直接就是把它的balance全转出来,那这个当然也是可以的,对吧?啊,那这里的话,这个this好像哦,他没有错啊。
36:10
大家如果要是呃想要用更好的办法的话,就是可以用这种方式啊,就是address.this就是更新的,呃,编译器可能会认这种方法,但是在这里的话,大家可以不用,就直接this.balance就可以,所以它就会把我们当前的所有余额全提出来,只要你给一个图,他就全提出来了,好,那我们这就是把这个。我们的voing这个合约全部做了一个实现,大家可以看到一共才70号,所以其实还是很简单的一个合约,对吧。
我来说两句